Ejemplo n.º 1
0
	def _split(action):
		"""
		Splits passed action so it can be displayed in UI.
		Returns (sucess, half, full, analog), with three actions
		for each UI element.
		Note that each returned action may be TriggerAction.
		
		If passed action cannot be decoded,
		'sucess' element of tuple is set to False
		"""
		half, full, analog = NoAction(), NoAction(), NoAction()
		actions = action.actions if isinstance(action, MultiAction) else [ action ]
		for a in actions:
			effective = TriggerComponent._strip_trigger(a).strip()
			if isinstance(effective, AxisAction):
				if analog:
					# UI can do only one analog action per trigger
					return False, half, full, analog
				analog = a
			elif isinstance(effective, MouseAction):
				if analog:
					# UI can do only one analog action per trigger
					return False, half, full, analog
				analog = a
			elif isinstance(a, TriggerAction):
				if full and half:
					# UI can handle only one full and
					# one half-press action
					return False, half, full, analog
				if a.release_level == TRIGGER_MAX:
					if full and a.press_level < full.press_level:
						if half:
							# UI can handle only one half-press action
							return False, half, full, analog
						half = a
					elif full:
						if half:
							# UI can handle only one half-press action
							return False, half, full, analog
						half, full = full, a
					else:
						full = a
				else:
					if half:
						# UI can handle only one half-press action
						return False, half, full, analog
					half = a
			elif isinstance(a, HipfireAction):
				hipfire_actions = TriggerComponent._strip_hipfire(a)
				half, full = (x.strip() for x in hipfire_actions )

			elif isinstance(a, NoAction):
				# Ignore theese
				pass
			else:
				# Unhandled action type
				return False, half, full, analog
		if full and not half:
			full, half = NoAction(), full
		return True, half, full, analog
    def test_noaction_is_false(self):
        """
		Tests if None can be used as False boolean value.
		"""
        assert not NoAction()
        if NoAction():
            raise Exception("NoAction is True :(")
Ejemplo n.º 3
0
	def set_action(self, mode, action):
		self.half, self.full, self.analog = NoAction(), NoAction(), NoAction()
		sucess, half, full, analog = TriggerComponent._split(action)
		if sucess:
			self._recursing = True
			cb = self.builder.get_object("cbActionType")
			if isinstance(action, HipfireAction):
				self.half, self.full = (TriggerComponent._strip_hipfire(x) for x in (half, full))
				if half and full:
					self.builder.get_object("sclPartialLevel").set_value(action.partialpress_level)
					self.builder.get_object("sclFullLevel").set_value(action.fullpress_level)
					trigger_style = action.mode
					self.set_cb(cb, "HIPFIRE_" + trigger_style, 1)
					self.builder.get_object("sclTimeOut").set_value(action.timeout)

			else:
				self.half, self.full, self.analog = (TriggerComponent._strip_trigger(x) for x in (half, full, analog))
				if half:
					self.builder.get_object("sclPartialLevel").set_value(half.press_level)
					trigger_style = "NORMAL_EXCLUSIVE" if (half.release_level < TRIGGER_MAX) else "NORMAL"
					self.set_cb(cb, trigger_style, 1)
				if full:
					self.builder.get_object("sclFullLevel").set_value(full.press_level)
				if isinstance(analog, TriggerAction):
					self.builder.get_object("sclARangeStart").set_value(analog.press_level)
					self.builder.get_object("sclARangeEnd").set_value(analog.release_level)
			
			self._recursing = False
		self.update()
Ejemplo n.º 4
0
	def update(self):
		cb = self.builder.get_object("cbActionType")
		key = cb.get_model().get_value(cb.get_active_iter(), 1)
		if key == "dpad8":
			# 8-way dpad
			self.editor.set_action(DPad8Action(*self.actions))
		elif key == "dpad":
			# 4-way dpad
			self.editor.set_action(DPadAction(*self.actions[0:4]))
		elif key == "wsad":
			# special case of 4-way dpad
			a = DPadAction(ButtonAction(Keys.KEY_W), ButtonAction(Keys.KEY_S),
				ButtonAction(Keys.KEY_A), ButtonAction(Keys.KEY_D))
			self.actions = [ NoAction() ] * 8
			self.editor.set_action(a)
			self.update_button_desc(a)
		elif key == "arrows":
			# special case of 4-way dpad
			a = DPadAction(ButtonAction(Keys.KEY_UP),
				ButtonAction(Keys.KEY_DOWN), ButtonAction(Keys.KEY_LEFT),
				ButtonAction(Keys.KEY_RIGHT))
			self.actions = [ NoAction() ] * 8
			self.editor.set_action(a)
			self.update_button_desc(a)
		else:
			# Menu
			self.on_cbMenus_changed()
Ejemplo n.º 5
0
    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()
Ejemplo n.º 6
0
 def __init__(self, app, editor):
     AEComponent.__init__(self, app, editor)
     BindingEditor.__init__(self, app)
     self._recursing = False
     self.half = NoAction()
     self.full = NoAction()
     self.analog = NoAction()
Ejemplo n.º 7
0
    def __init__(self, *stuff):
        Modifier.__init__(self)
        self.default = None
        self.mods = {}
        self.held_buttons = set()
        self.held_sticks = set()
        self.held_triggers = {}
        self.order = []
        self.old_gyro = 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):
                if button is None:
                    self.default = i
                    continue
                self.mods[button] = i
                self.order.append(button)
                button = None
            elif i in SCButtons:
                button = i
            else:
                raise ValueError("Invalid parameter for 'mode': %s" % (i, ))
        if self.default is None:
            self.default = NoAction()
    def load_circular_action(self, action):
        cbAxisOutput = self.builder.get_object("cbAxisOutput")
        btCircularAxis = self.builder.get_object("btCircularAxis")
        btCircularButton0 = self.builder.get_object("btCircularButton0")
        btCircularButton1 = self.builder.get_object("btCircularButton1")

        # Turn action into list of subactions (even if it's just single action)
        if isinstance(action.action, MultiAction):
            actions = action.action.actions
        else:
            actions = [action.action]

        # Parse that list
        self.circular_axis, self.circular_buttons = NoAction(), [None, None]
        for action in actions:
            if isinstance(action, ButtonAction):
                self.circular_buttons = [action.button, action.button2]
            else:
                self.circular_axis = action

        # Set labels
        b0, b1 = self.circular_buttons
        btCircularButton0.set_label(ButtonAction.describe_button(b0))
        btCircularButton1.set_label(ButtonAction.describe_button(b1))
        btCircularAxis.set_label(self.circular_axis.describe(Action.AC_PAD))

        self.set_cb(cbAxisOutput, "circular", 2)
Ejemplo n.º 9
0
    def set_input(self, id, action, mode=Action.AC_BUTTON):
        btDefault = self.builder.get_object("btDefault")
        self.id = id
        self.mode = mode

        self.set_title("Modeshift for %s" %
                       (id.name if id in SCButtons else str(id), ))

        if isinstance(action, ModeModifier):
            self._load_modemod(0, action)
            self._set_nomod_button(0, action.default)
            self._set_nomod_button(1, NoAction())
            self._set_nomod_button(2, NoAction())
        elif isinstance(action, DoubleclickModifier):  # includes HoldModifier
            self._set_nomod_button(0, action.normalaction)
            self._set_nomod_button(1, action.holdaction)
            self._set_nomod_button(2, action.action)
        self.builder.get_object("adjTime").set_value(action.timeout)

        if mode == Action.AC_OSK:
            # This is kinda bad, but allowing Custom Editor
            # from OSK editor is in TODO
            self.builder.get_object("btCustomActionEditor").set_visible(False)
        if mode != Action.AC_BUTTON:
            for w in ("vbHold", "vbDoubleClick", "lblHold", "lblDoubleClick"):
                self.builder.get_object(w).set_sensitive(False)
Ejemplo n.º 10
0
    def from_json_data(self, data, key=None):
        """
		Converts dict stored in profile file into action.
		
		May throw ParseError.
		"""
        if key is not None:
            # Don't fail if called for non-existent key, return NoAction instead.
            # Using this is sorter than
            # calling 'if button in data["buttons"]: ...' everywhere
            if key in data:
                return self.from_json_data(data[key], None)
            else:
                return NoAction()

        if "action" in data:
            a = self.restart(data["action"]).parse() or NoAction()
        else:
            a = NoAction()
        decoders = set()
        for key in data:
            if key in Action.PKEYS:
                decoders.add(Action.PKEYS[key])

        if decoders:
            for cls in sorted(decoders, key=lambda a: a.PROFILE_KEY_PRIORITY):
                a = cls.decode(data, a, self,
                               0)  # Profile version is not yet used anywhere
        return a
 def on_btClearCircularButtons_clicked(self, *a):
     btCircularButton0 = self.builder.get_object("btCircularButton0")
     btCircularButton1 = self.builder.get_object("btCircularButton1")
     self.circular_buttons = [None, None]
     btCircularButton0.set_label(NoAction().describe(Action.AC_PAD))
     btCircularButton1.set_label(NoAction().describe(Action.AC_PAD))
     self.editor.set_action(self.make_circular_action())
Ejemplo n.º 12
0
	def __init__(self, parser):
		self.parser = parser
		self.buttons = { x : NoAction() for x in SCButtons }
		self.menus = {}
		self.stick = NoAction()
		self.triggers = { Profile.LEFT : NoAction(), Profile.RIGHT : NoAction() }
		self.pads = { Profile.LEFT : NoAction(), Profile.RIGHT : NoAction() }
		self.gyro = NoAction()
Ejemplo n.º 13
0
 def __init__(self, app, callback):
     self.app = app
     self.id = None
     self.mode = Action.AC_BUTTON
     self.ac_callback = callback
     self.current_page = 0
     self.actions = ([], [], [])
     self.nomods = [NoAction(), NoAction(), NoAction()]
     self.setup_widgets()
Ejemplo n.º 14
0
 def __init__(self, doubleclickaction, normalaction=None):
     Modifier.__init__(self)
     self.action = doubleclickaction
     self.normalaction = normalaction or NoAction()
     self.holdaction = NoAction()
     self.timeout = self.DEAFAULT_TIMEOUT
     self.waiting = False
     self.pressed = False
     self.active = None
Ejemplo n.º 15
0
 def __init__(self, app, callback):
     Editor.__init__(self)
     self.app = app
     self.id = None
     self.mode = Action.AC_BUTTON
     self.ac_callback = callback
     self.radius = 0.5
     self.actions = [NoAction(), NoAction()]
     self.setup_widgets()
Ejemplo n.º 16
0
	def on_btAddAction_clicked(self, *a):
		cbButtonChooser = self.builder.get_object("cbButtonChooser")
		item = cbButtonChooser.get_model().get_value(cbButtonChooser.get_active_iter(), 0)
		if item.startswith("Soft"):
			b = getattr(SCButtons, item.split(" ")[-1])
			rng = RangeOP(b, ">=", -1)
			self._add_action(self.current_page, rng, NoAction())
		else:
			b = getattr(SCButtons, item)
			self._add_action(self.current_page, b, NoAction())
Ejemplo n.º 17
0
 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)
Ejemplo n.º 18
0
 def __init__(self, doubleclickaction, normalaction=None, time=None):
     Modifier.__init__(self)
     HapticEnabledAction.__init__(self)
     self.action = doubleclickaction
     self.normalaction = normalaction or NoAction()
     self.holdaction = NoAction()
     self.actions = (self.action, self.normalaction, self.holdaction)
     self.timeout = time or DoubleclickModifier.DEAFAULT_TIMEOUT
     self.waiting = False
     self.pressed = False
     self.active = None
Ejemplo n.º 19
0
	def from_json_data(self, data, key=None):
		"""
		Converts dict stored in profile file into action.
		
		May throw ParseError.
		"""
		if key is not None:
			# Don't fail if called for non-existent key, return NoAction instead.
			# Using this is sorter than
			# calling 'if button in data["buttons"]: ...' everywhere
			if key in data:
				return self.from_json_data(data[key], None)
			else:
				return NoAction()
		
		a = NoAction()
		if "action" in data:
			a = self.restart(data["action"]).parse() or NoAction()
		if "X" in data or "Y" in data:
			# "action" is ignored if either "X" or "Y" is there
			x = self.from_json_data(data["X"]) if "X" in data else NoAction()
			y = self.from_json_data(data["Y"]) if "Y" in data else NoAction()
			a = XYAction(x, y)
		if "deadzone" in data:
			lower = data["deadzone"]["lower"] if "lower" in data["deadzone"] else STICK_PAD_MIN
			upper = data["deadzone"]["upper"] if "upper" in data["deadzone"] else STICK_PAD_MAX
			a = DeadzoneModifier(lower, upper, a)
		if "sensitivity" in data:
			args = data["sensitivity"]
			args.append(a)
			a = SensitivityModifier(*args)
		if "feedback" in data:
			args = data["feedback"]
			if hasattr(HapticPos, args[0]):
				args[0] = getattr(HapticPos, args[0])
			args.append(a)
			a = FeedbackModifier(*args)
		if "osd" in data:
			a = OSDAction(a)
			if data["osd"] is not True:
				a.timeout = float(data["osd"])
		if "click" in data:
			a = ClickModifier(a)
		if "name" in data:
			a.name = data["name"]
		if "modes" in data:
			args = []
			for button in data['modes']:
				if hasattr(SCButtons, button):
					args += [ getattr(SCButtons, button), self.from_json_data(data['modes'][button]) ]
			if a:
				args += [ a ]
			a = ModeModifier(*args)
		return a
Ejemplo n.º 20
0
 def __init__(self, parser):
     self.parser = parser
     self.buttons = {x: NoAction() for x in SCButtons}
     self.menus = {}
     self.stick = NoAction()
     self.triggers = {Profile.LEFT: NoAction(), Profile.RIGHT: NoAction()}
     self.pads = {Profile.LEFT: NoAction(), Profile.RIGHT: NoAction()}
     self.gyro = NoAction()
Ejemplo n.º 21
0
 def _make_action(self):
     """ Generates and returns Action instance """
     cbMode = self.builder.get_object("cbMode")
     key = cbMode.get_model().get_value(cbMode.get_active_iter(), 0)
     if key == "inner":
         return MultiAction(
             self.actions[1],
             RingAction(self.radius, self.actions[0], NoAction()))
     elif key == "outer":
         return MultiAction(
             self.actions[0],
             RingAction(self.radius, NoAction(), self.actions[1]))
     else:
         return RingAction(self.radius, *self.actions)
Ejemplo n.º 22
0
	def clear(self):
		""" Clears all actions and adds default menu action on center button """
		self.buttons = { x : NoAction() for x in SCButtons }
		self.buttons[SCButtons.C] = HoldModifier(
			MenuAction("Default.menu"),
			normalaction = MenuAction("Default.menu")
		)
		self.menus = {}
		self.stick = NoAction()
		self.is_template = False
		self.triggers = { Profile.LEFT : NoAction(), Profile.RIGHT : NoAction() }
		self.pads = { Profile.LEFT : NoAction(),
				Profile.RIGHT : NoAction(), Profile.CPAD : NoAction() }
		self.gyro = NoAction()
Ejemplo n.º 23
0
    def _set_mode(self, mode, id, action):
        btDefault = self.builder.get_object("btDefault")
        self.id = id
        self.mode = mode

        if isinstance(action, ModeModifier):
            self._load_modemod(0, action)
            self._set_nomod_button(0, action.default)
            self._set_nomod_button(1, NoAction())
            self._set_nomod_button(2, NoAction())
        elif isinstance(action, DoubleclickModifier):  # includes HoldModifier
            self._set_nomod_button(0, action.normalaction)
            self._set_nomod_button(1, action.holdaction)
            self._set_nomod_button(2, action.action)
Ejemplo n.º 24
0
	def __init__(self, *parameters):
		# TODO: remove self.speeds
		self.speeds = []
		action = NoAction()
		for p in parameters:
			if type(p) in (int, float) and len(self.speeds) < 3:
				self.speeds.append(float(p))
			else:
				if isinstance(p, Action):
					action = p
		while len(self.speeds) < 3:
			self.speeds.append(1.0)
		Modifier.__init__(self, action)
		action.set_speed(*self.speeds)
		self.parameters = parameters
Ejemplo n.º 25
0
 def __init__(self, *parameters):
     # TODO: remove self.speeds
     self.speeds = []
     action = NoAction()
     for p in parameters:
         if type(p) in (int, float) and len(self.speeds) < 3:
             self.speeds.append(float(p))
         else:
             if isinstance(p, Action):
                 action = p
     while len(self.speeds) < 3:
         self.speeds.append(1.0)
     Modifier.__init__(self, action)
     action.set_speed(*self.speeds)
     self.parameters = parameters
Ejemplo n.º 26
0
    def set_input(self, id, action, mode=None):
        btDefault = self.builder.get_object("btDefault")
        lblPressAlone = self.builder.get_object("lblPressAlone")
        cbHoldFeedback = self.builder.get_object("cbHoldFeedback")
        sclHoldFeedback = self.builder.get_object("sclHoldFeedback")

        self.id = id
        self._fill_button_chooser()

        if id in STICKS:
            lblPressAlone.set_label(_("(no button pressed)"))
            self.mode = mode = mode or Action.AC_STICK
        elif id in PADS:
            lblPressAlone.set_label(_("(no button pressed)"))
            self.mode = mode = mode or Action.AC_PAD
        else:
            lblPressAlone.set_label(_("(pressed alone)"))
            self.mode = mode = mode or Action.AC_BUTTON

        self.set_title("Modeshift for %s" %
                       (nameof(id) if id in SCButtons else str(id), ))

        if isinstance(action, FeedbackModifier):
            cbHoldFeedback.set_active(True)
            sclHoldFeedback.set_value(action.haptic.get_amplitude())
            action = action.action
        else:
            cbHoldFeedback.set_active(False)
            sclHoldFeedback.set_value(512)

        if isinstance(action, ModeModifier):
            self._load_modemod(0, action)
            self._set_nomod_button(0, action.default)
            self._set_nomod_button(1, NoAction())
            self._set_nomod_button(2, NoAction())
        elif isinstance(action, DoubleclickModifier):  # includes HoldModifier
            self._set_nomod_button(0, action.normalaction)
            self._set_nomod_button(1, action.holdaction)
            self._set_nomod_button(2, action.action)
        self.builder.get_object("adjTime").set_value(action.timeout)

        if mode == Action.AC_OSK:
            # This is kinda bad, but allowing Custom Editor
            # from OSK editor is in TODO
            self.builder.get_object("btCustomActionEditor").set_visible(False)
        if mode != Action.AC_BUTTON:
            for w in ("vbHold", "vbDoubleClick", "lblHold", "lblDoubleClick"):
                self.builder.get_object(w).set_sensitive(False)
Ejemplo n.º 27
0
 def clear(self):
     """ Clears all actions and adds default menu action on center button """
     self.buttons = {x: NoAction() for x in SCButtons}
     self.buttons[SCButtons.C] = HoldModifier(
         MenuAction("Default.menu"),
         normalaction=MenuAction("Default.menu"))
     self.menus = {}
     self.stick = NoAction()
     self.is_template = False
     self.triggers = {Profile.LEFT: NoAction(), Profile.RIGHT: NoAction()}
     self.pads = {Profile.LEFT: NoAction(), Profile.RIGHT: NoAction()}
     self.gyro = NoAction()
Ejemplo n.º 28
0
 def on_btAddAction_clicked(self, *a):
     cbButtonChooser = self.builder.get_object("cbButtonChooser")
     b = getattr(
         SCButtons,
         cbButtonChooser.get_model().get_value(
             cbButtonChooser.get_active_iter(), 0))
     self._add_action(self.current_page, b, NoAction())
Ejemplo n.º 29
0
	def __init__(self, app, editor):
		AEComponent.__init__(self, app, editor)
		MenuActionCofC.__init__(self)
		BindingEditor.__init__(self, app)
		self._recursing = False
		self._userdata_load_started = False
		self.actions = [ NoAction() ] * 8
Ejemplo n.º 30
0
	def on_cbActionType_changed(self, *a):
		cbActionType = self.builder.get_object("cbActionType")
		stActionData = self.builder.get_object("stActionData")
		key = cbActionType.get_model().get_value(cbActionType.get_active_iter(), 0)
		if key == "shell":
			stActionData.set_visible_child(self.builder.get_object("vbShell"))
			self.on_enCommand_changed()
		elif key == "profile":
			stActionData.set_visible_child(self.builder.get_object("vbProfile"))
			self.on_cbProfile_changed()
		elif key == "keyboard":
			stActionData.set_visible_child(self.builder.get_object("nothing"))
			if not self._recursing:
				self.editor.set_action(KeyboardAction())
		elif key == "resetgyro":
			stActionData.set_visible_child(self.builder.get_object("nothing"))
			if not self._recursing:
				self.editor.set_action(ResetGyroAction())
		elif key == "osd":
			stActionData.set_visible_child(self.builder.get_object("vbOSD"))
			if not self._recursing:
				self.editor.set_action(OSDAction(""))
		elif key == "menu":
			stActionData.set_visible_child(self.builder.get_object("grMenu"))
			self.on_cbMenus_changed()
		elif key == "turnoff":
			stActionData.set_visible_child(self.builder.get_object("nothing"))
			if not self._recursing:
				self.editor.set_action(TurnOffAction())
		else: # none
			stActionData.set_visible_child(self.builder.get_object("nothing"))
			if not self._recursing:
				self.editor.set_action(NoAction())
Ejemplo n.º 31
0
	def set_action(self, profile, id, action):
		"""
		Stores action in profile.
		Returns formely stored action.
		"""
		before = NoAction()
		if id == SCButtons.STICKPRESS and Profile.STICK in self.button_widgets:
			before, profile.buttons[id] = profile.buttons[id], action
			self.button_widgets[Profile.STICK].update()
		elif id in BUTTONS:
			before, profile.buttons[id] = profile.buttons[id], action
			self.button_widgets[id].update()
		elif id in PRESSABLE:
			before, profile.buttons[id] = profile.buttons[id], action
			self.button_widgets[id.name].update()
		elif id in TRIGGERS:
			before, profile.triggers[id] = profile.triggers[id], action
			self.button_widgets[id].update()
		elif id in GYROS:
			before, profile.gyro = profile.gyro, action
			self.button_widgets[id].update()
		elif id in STICKS + PADS:
			if id in STICKS:
				before, profile.stick = profile.stick, action
			elif id == "LPAD":
				before, profile.pads[Profile.LEFT] = profile.pads[Profile.LEFT], action
			else:
				before, profile.pads[Profile.RIGHT] = profile.pads[Profile.RIGHT], action
			self.button_widgets[id].update()
		return before
Ejemplo n.º 32
0
    def send(self, *a):
        if self._recursing: return

        cbMode = self.builder.get_object("cbMode")
        cbYawRoll = self.builder.get_object("cbYawRoll")
        cbGyroButton = self.builder.get_object("cbGyroButton")
        cbInvertGyro = self.builder.get_object("cbInvertGyro")
        action = cbMode.get_model().get_value(cbMode.get_active_iter(), 0)
        key = cbMode.get_model().get_value(cbMode.get_active_iter(), 2)
        yawroll = cbYawRoll.get_model().get_value(cbYawRoll.get_active_iter(),
                                                  0)
        button = cbGyroButton.get_model().get_value(
            cbGyroButton.get_active_iter(), 0)

        match = re.match(r"([^\[]+)\[([^\|]+)\|([^\]]+)\](.*)", action)
        if match:
            grps = match.groups()
            if yawroll == YAW:
                action = "%s%s%s" % (grps[0], grps[1], grps[3])
            else:
                action = "%s%s%s" % (grps[0], grps[2], grps[3])
        action = self.parser.restart(action).parse()

        if button and action:
            if cbInvertGyro.get_active():
                action = ModeModifier(getattr(SCButtons, button), NoAction(),
                                      action)
            else:
                action = ModeModifier(getattr(SCButtons, button), action)
        if key == "mouse":
            self.editor.set_default_sensitivity(3.5, 3.5, 3.5)
        else:
            self.editor.set_default_sensitivity(1, 1, 1)

        self.editor.set_action(action)
Ejemplo n.º 33
0
    def send(self, *a):
        if self._recursing: return

        cbGyroButton = self.builder.get_object("cbGyroButton")
        button = cbGyroButton.get_model().get_value(
            cbGyroButton.get_active_iter(), 0)

        normal, n_set = [None, None, None], False
        absolute, a_set = [None, None, None], False

        for i in xrange(0, 3):
            if self.axes[i] is not None:
                if self.cbs[i].get_active():
                    absolute[i] = self.axes[i]
                    a_set = True
                else:
                    normal[i] = self.axes[i]
                    n_set = True

        if n_set and a_set:
            action = MultiAction(GyroAction(*normal), GyroAbsAction(*absolute))
        elif n_set:
            action = GyroAction(*normal)
        elif a_set:
            action = GyroAbsAction(*absolute)
        else:
            action = NoAction()

        if button and action:
            action = ModeModifier(getattr(SCButtons, button), action)

        self.editor.set_action(action)
Ejemplo n.º 34
0
	def __init__(self, *stuff):
		Modifier.__init__(self)
		self.default = None
		self.mods = {}
		self.held_buttons = set()
		self.held_sticks = set()
		self.held_triggers = {}
		self.order = []
		self.old_gyro = None
		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):
				if button is None:
					self.default = i
					continue
				self.mods[button] = i
				self.order.append(button)
				button = None
			elif i in SCButtons:
				button = i
			else:
				raise ValueError("Invalid parameter for 'mode': %s" % (i,))
		if self.default is None:
			self.default = NoAction()
Ejemplo n.º 35
0
	def load_circular_action(self, action):
		cbAxisOutput = self.builder.get_object("cbAxisOutput")
		btCircularAxis = self.builder.get_object("btCircularAxis")
		btCircularButton0 = self.builder.get_object("btCircularButton0")
		btCircularButton1 = self.builder.get_object("btCircularButton1")
		
		# Turn action into list of subactions (even if it's just single action)
		if isinstance(action.action, MultiAction):
			actions = action.action.actions
		else:
			actions = [ action.action ]
		
		# Parse that list
		self.circular_axis, self.circular_buttons = NoAction(), [ None, None ]
		for action in actions:
			if isinstance(action, ButtonAction):
				self.circular_buttons = [ action.button, action.button2 ]
			else:
				self.circular_axis = action
		
		# Set labels
		b0, b1 = self.circular_buttons
		btCircularButton0.set_label(ButtonAction.describe_button(b0))
		btCircularButton1.set_label(ButtonAction.describe_button(b1))
		btCircularAxis.set_label(self.circular_axis.describe(Action.AC_PAD))
		
		self.set_cb(cbAxisOutput, "circular", 2)
Ejemplo n.º 36
0
	def __init__(self, app, callback):
		Editor.__init__(self)
		self.app = app
		self.id = None
		self.components = []			# List of available components
		self.loaded_components = {}		# by class name
		self.c_buttons = {} 			# Component-to-button dict
		self.sens_widgets = []			# Sensitivity sliders, labels and 'clear' buttons
		self.feedback_widgets = []		# Feedback settings sliders, labels and 'clear' buttons, plus default value as last item
		self.smoothing_widgets = []		# Smoothing settings sliders, labels and 'clear' buttons, plus default value as last item
		self.deadzone_widgets = []		# Deadzone settings sliders, labels and 'clear' buttons, plus default value as last item
		self.sens = [1.0] * 3			# Sensitivity slider values
		self.sens_defaults = [1.0] * 3	# Clear button clears to this
		self.feedback = [0.0] * 3		# Feedback slider values, set later
		self.deadzone = [0] * 2			# Deadzone slider values, set later
		self.deadzone_mode = None		# None for 'disabled'
		self.feedback_position = None	# None for 'disabled'
		self.smoothing = None			# None for 'disabled'
		self.click = False				# Click modifier value. None for disabled
		self.rotation_angle = 0			# RotateInputModifier angle
		self.osd = False				# 'OSD enabled' value.
		self.setup_widgets()
		self.load_components()
		self.ac_callback = callback		# This is different callback than ButtonChooser uses
		Editor.install_error_css()
		self._action = NoAction()
		self._replaced_action = None
		self._selected_component = None
		self._modifiers_enabled = True
		self._multiparams = [ None ] * 8
		self._mode = None
		self._recursing = False
	def __init__(self, app, callback):
		self.app = app
		self.id = None
		self.mode = Action.AC_BUTTON
		self.ac_callback = callback
		self.setup_widgets()
		self.actions = []
		self.default = NoAction()
Ejemplo n.º 38
0
	def on_btSave_clicked(self, *a):
		tvItems = self.builder.get_object("tvItems")
		cbProfile = self.builder.get_object("cbProfile")
		entTitle = self.builder.get_object("entTitle")
		entClass = self.builder.get_object("entClass")
		cbMatchTitle = self.builder.get_object("cbMatchTitle")
		cbMatchClass = self.builder.get_object("cbMatchClass")
		cbExactTitle = self.builder.get_object("cbExactTitle")
		cbRegExp = self.builder.get_object("cbRegExp")
		rbProfile = self.builder.get_object("rbProfile")
		rbTurnOff = self.builder.get_object("rbTurnOff")
		rbRestart = self.builder.get_object("rbRestart")
		ce = self.builder.get_object("ConditionEditor")
		
		# Build condition
		data = {}
		if cbMatchTitle.get_active() and entTitle.get_text():
			if cbExactTitle.get_active():
				data['exact_title'] = entTitle.get_text()
			elif cbRegExp.get_active():
				data['regexp'] = entTitle.get_text()
			else:
				data['title'] = entTitle.get_text()
		if cbMatchClass.get_active() and entClass.get_text():
			data['wm_class'] = entClass.get_text()
		condition = Condition(**data)
		
		# Grab selected action
		model, iter = cbProfile.get_model(), cbProfile.get_active_iter()
		action = NoAction()
		if rbProfile.get_active():
			action = ChangeProfileAction(model.get_value(iter, 0))
		elif rbTurnOff.get_active():
			action = TurnOffAction()
		elif rbRestart.get_active():
			action = RestartDaemonAction()
		
		# Grab & update current row
		model, iter = tvItems.get_selection().get_selected()
		o = model.get_value(iter, 0)
		o.condition = condition
		o.action = action
		model.set_value(iter, 1, condition.describe())
		model.set_value(iter, 2, action.describe(Action.AC_SWITCHER))
		self.hide_dont_destroy(ce)
		self.save_config()
Ejemplo n.º 39
0
	def __init__(self, doubleclickaction, normalaction=None):
		Modifier.__init__(self)
		self.action = doubleclickaction
		self.normalaction = normalaction or NoAction()
		self.holdaction = NoAction()
		self.timeout = self.DEAFAULT_TIMEOUT
		self.waiting = False
		self.pressed = False
		self.active = None
Ejemplo n.º 40
0
	def __init__(self, doubleclickaction, normalaction=None, time=None):
		Modifier.__init__(self)
		HapticEnabledAction.__init__(self)
		self.action = doubleclickaction
		self.normalaction = normalaction or NoAction()
		self.holdaction = NoAction()
		self.actions = ( self.action, self.normalaction, self.holdaction )
		self.timeout = time or DoubleclickModifier.DEAFAULT_TIMEOUT
		self.waiting_task = None
		self.pressed = False
		self.active = None
Ejemplo n.º 41
0
	def __init__(self, app, editor):
		AEComponent.__init__(self, app, editor)
		TimerManager.__init__(self)
		self._recursing = False
		self.relative_area = False
		self.osd_area_instance = None
		self.on_wayland = False
		self.circular_axis = MouseAction(Rels.REL_WHEEL)
		self.circular_buttons = [ None, None ]
		self.button = None
		self.parser = GuiActionParser()
Ejemplo n.º 42
0
	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)
Ejemplo n.º 43
0
	def _make_action(self):
		""" Generates and returns Action instance """
		entName = self.builder.get_object("entName")
		cbMacroType = self.builder.get_object("cbMacroType")
		pars = [ x[0] for x in self.actions ]
		if len(pars) == 0:
			# No action is actually set
			action = NoAction()
		elif cbMacroType.get_active() == 2:
			# Cycle
			pars = filter(lambda a : not isinstance(a, SleepAction), pars)
			action = Cycle(*pars)
		elif cbMacroType.get_active() == 1:
			# Repeating macro
			action = Macro(*pars)
			action.repeat = True
		elif len(pars) == 1:
			# Only one action
			action = pars[0]
		else:
			# Macro
			action = Macro(*pars)
		if entName.get_text().decode("utf-8").strip() != "":
			action.name = entName.get_text().decode("utf-8").strip()
		return action
Ejemplo n.º 44
0
	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()
Ejemplo n.º 45
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__
class ModeshiftEditor(Editor):
	GLADE = "modeshift_editor.glade"
	BUTTONS = (	# in order as displayed in combobox
		(SCButtons.A,			_('A') ),
		(SCButtons.B,			_('B') ),
		(SCButtons.X,			_('X') ),
		(SCButtons.Y,			_('Y') ),
		(None, None),
		(SCButtons.BACK,		_('Back (select)') ),
		(SCButtons.C,			_('Center') ),
		(SCButtons.START,		_('Start') ),
		(None, None),
		(SCButtons.LGRIP,		_('Left Grip') ),
		(SCButtons.RGRIP,		_('Right Grip') ),
		(None, None),
		(SCButtons.LB,			_('Left Bumper') ),
		(SCButtons.RB,			_('Right Bumper') ),
		(SCButtons.LT,			_('Left Trigger') ),
		(SCButtons.RT,			_('Right Trigger') ),
		(None, None),
		(SCButtons.LPAD,		_('Left Pad Pressed') ),
		(SCButtons.RPAD,		_('Right Pad Pressed') ),
		(SCButtons.LPADTOUCH,	_('Left Pad Touched') ),
		(SCButtons.RPADTOUCH,	_('Right Pad Touched') ),
	)
	
	def __init__(self, app, callback):
		self.app = app
		self.id = None
		self.mode = Action.AC_BUTTON
		self.ac_callback = callback
		self.setup_widgets()
		self.actions = []
		self.default = NoAction()
	
	
	def setup_widgets(self):
		self.builder = Gtk.Builder()
		self.builder.add_from_file(os.path.join(self.app.gladepath, self.GLADE))
		self.window = self.builder.get_object("Dialog")
		self.builder.connect_signals(self)
		
		cbButtonChooser = self.builder.get_object("cbButtonChooser")
		cbButtonChooser.set_row_separator_func( lambda model, iter : model.get_value(iter, 0) is None )
		model = cbButtonChooser.get_model()
		for button, text in self.BUTTONS:
			model.append(( None if button is None else button.name, text ))
		cbButtonChooser.set_active(0)
		headerbar(self.builder.get_object("header"))
	
	
	def _add_action(self, button, action):
		grActions = self.builder.get_object("grActions")
		cbButtonChooser = self.builder.get_object("cbButtonChooser")
		model = cbButtonChooser.get_model()
		
		for row in model:
			if model.get_value(row.iter, 0) == button.name:
				model.remove(row.iter)
				break
		try:
			while model.get_value(model[0].iter, 0) is None:
				model.remove(model[0].iter)
			cbButtonChooser.set_active(0)
		except: pass
		
		i = len(self.actions) + 1
		l = Gtk.Label()
		l.set_markup("<b>%s</b>" % (button.name,))
		l.set_xalign(0.0)
		b = Gtk.Button.new_with_label(action.describe(self.mode))
		b.set_property("hexpand", True)
		b.connect('clicked', self.on_actionb_clicked, button)
		clearb = Gtk.Button()
		clearb.set_image(Gtk.Image.new_from_stock("gtk-delete", Gtk.IconSize.SMALL_TOOLBAR))
		clearb.set_relief(Gtk.ReliefStyle.NONE)
		clearb.connect('clicked', self.on_clearb_clicked, button)
		grActions.attach(l,			0, i, 1, 1)
		grActions.attach(b,			1, i, 1, 1)
		grActions.attach(clearb,	2, i, 1, 1)
		
		self.actions.append([ button, action, l, b, clearb ])
		grActions.show_all()
	
	
	def on_clearb_clicked(self, trash, button):
		grActions = self.builder.get_object("grActions")
		cbButtonChooser = self.builder.get_object("cbButtonChooser")
		model = cbButtonChooser.get_model()
		# Remove requested action from the list
		for i in xrange(0, len(self.actions)):
			if self.actions[i][0] == button:
				button, action, l, b, clearb = self.actions[i]
				for w in (l, b, clearb): grActions.remove(w)
				del self.actions[i]
				break
		# Move everything after that action one position up
		# - remove it
		for j in xrange(i, len(self.actions)):
			button, action, l, b, clearb = self.actions[j]
			for w in (l, b, clearb): grActions.remove(w)
		# - add it again
		for j in xrange(i, len(self.actions)):
			button, action, l, b, clearb = self.actions[j]
			grActions.attach(l,			0, j + 1, 1, 1)
			grActions.attach(b,			1, j + 1, 1, 1)
			grActions.attach(clearb,	2, j + 1, 1, 1)
		# Regenereate combobox with removed button added back to it
		# - Store acive item from in combobox
		active, i, index = None, 0, -1
		try:
			active = model.get_value(cbButtonChooser.get_active_iter(), 0)
		except: pass
		# Clear entire combobox
		model.clear()
		# Fill it again
		for button, text in self.BUTTONS:
			model.append(( None if button is None else button.name, text ))
			if button is not None:
				if button.name == active:
					index = i
			i += 1
		# Reselect formely active item
		if index >= 0:
			cbButtonChooser.set_active(index)
	
	
	def _setup_editor(self, ae, action):
		if self.mode == Action.AC_BUTTON:
			ae.set_button(self.id, action)
		elif self.mode == Action.AC_TRIGGER:
			ae.set_trigger(self.id, action)
		elif self.mode == Action.AC_STICK:
			ae.set_stick(action)
		elif self.mode == Action.AC_GYRO:
			ae.set_gyro(action)
		elif self.mode == Action.AC_PAD:
			ae.set_pad(self.id, action)		
	
	
	def _choose_editor(self, action, cb):
		if isinstance(action, Macro):
			from scc.gui.macro_editor import MacroEditor	# Cannot be imported @ top
			e = MacroEditor(self.app, cb)
			e.set_title(_("Edit Macro"))
		else:
			from scc.gui.action_editor import ActionEditor	# Cannot be imported @ top
			e = ActionEditor(self.app, cb)
			e.set_title(_("Edit Action"))
			e.hide_modeshift()
		return e

	
	def on_actionb_clicked(self, trash, clicked_button):
		for i in self.actions:
			button, action, l, b, clearb = i
			if button == clicked_button:
				def on_chosen(id, action, reopen=False):
					b.set_label(action.describe(self.mode))
					i[1] = action
					if reopen: self.on_actionb_clicked(trash, clicked_button)
				
				ae = self._choose_editor(action, on_chosen)
				self._setup_editor(ae, action)
				ae.show(self.window)
				return
	
	
	def on_btDefault_clicked(self, *a):
		btDefault = self.builder.get_object("btDefault")
		def on_chosen(id, action, reopen=False):
			btDefault.set_label(action.describe(self.mode))
			self.default = action
			if reopen: self.on_btDefault_clicked()
		
		ae = self._choose_editor(self.default, on_chosen)
		self._setup_editor(ae, self.default)
		ae.show(self.window)
	
	
	def on_btClearDefault_clicked(self, *a):
		self.default = NoAction()
		btDefault = self.builder.get_object("btDefault")
		btDefault.set_label(self.default.describe(self.mode))
	
	
	def on_btAddAction_clicked(self, *a):
		cbButtonChooser = self.builder.get_object("cbButtonChooser")
		b = getattr(SCButtons, cbButtonChooser.get_model().get_value(cbButtonChooser.get_active_iter(), 0))
		self._add_action(b, NoAction())
	
	
	def on_btClear_clicked	(self, *a):
		""" Handler for clear button """
		action = NoAction()
		if self.ac_callback is not None:
			self.ac_callback(self.id, action)
		self.close()
	
	
	def on_btOK_clicked(self, *a):
		""" Handler for OK button """
		pars = []
		for button, action, l, b, clearb in self.actions:
			pars += [ button, action ]
		if self.default:
			pars += [ self.default ]
		action = ModeModifier(*pars)
		if len(pars) == 0:
			# No action is actually set
			action = NoAction()
		elif len(pars) == 1:
			# Only default action left
			action = self.default
		if self.ac_callback is not None:
			self.ac_callback(self.id, action)
		self.close()
	
	
	def _set_mode(self, mode, id, action):
		btDefault = self.builder.get_object("btDefault")
		self.id = id
		self.mode = mode
		for key in action.mods:
			self._add_action(key, action.mods[key])
		self.default = action.default
		btDefault.set_label(self.default.describe(self.mode))
	
	
	def set_button(self, id, action):
		""" Setups editor as editor for button action """
		self._set_mode(Action.AC_BUTTON, id, action)
	
	
	def set_trigger(self, id, action):
		""" Setups editor as editor for trigger action """
		self._set_mode(Action.AC_TRIGGER, id, action)
	
	
	def set_stick(self, action):
		""" Setups action editor as editor for stick action """
		self._set_mode(Action.AC_STICK, Profile.STICK, action)
	
	
	def set_pad(self, id, action):
		""" Setups action editor as editor for pad action """
		self._set_mode(Action.AC_PAD, id, action)
	
	
	def set_gyro(self, action):
		""" Setups editor as editor for button action """
		self._set_mode(Action.AC_GYRO, Profile.GYRO, action)
	def on_btClearDefault_clicked(self, *a):
		self.default = NoAction()
		btDefault = self.builder.get_object("btDefault")
		btDefault.set_label(self.default.describe(self.mode))
Ejemplo n.º 48
0
class Profile(object):
	LEFT  = LEFT
	RIGHT = RIGHT
	WHOLE = WHOLE
	STICK = STICK
	GYRO  = GYRO
	X, Y, Z = "X", "Y", "Z"
	STICK_AXES = { X : "lpad_x", Y : "lpad_y" }
	LPAD_AXES  = STICK_AXES
	RPAD_AXES  = { X : "rpad_x", Y : "rpad_y" }
	TRIGGERS   = [ LEFT, RIGHT ]
	
	def __init__(self, parser):
		self.parser = parser
		self.buttons = { x : NoAction() for x in SCButtons }
		self.menus = {}
		self.stick = NoAction()
		self.triggers = { Profile.LEFT : NoAction(), Profile.RIGHT : NoAction() }
		self.pads = { Profile.LEFT : NoAction(), Profile.RIGHT : NoAction() }
		self.gyro = NoAction()
	
	
	def save(self, filename):
		""" Saves profile into file. Returns self """
		data = {
			'buttons'		: {},
			'stick'			: self.stick,
			'gyro'			: self.gyro,
			'trigger_left'	: self.triggers[Profile.LEFT],
			'trigger_right'	: self.triggers[Profile.RIGHT],
			"pad_left"		: self.pads[Profile.LEFT],
			"pad_right"		: self.pads[Profile.RIGHT],
			"menus"			: { id : self.menus[id].encode() for id in self.menus }
		}
		
		for i in self.buttons:
			if self.buttons[i]:
				data['buttons'][i.name] = self.buttons[i]
		
		# Generate & save json
		jstr = Encoder(sort_keys=True, indent=4).encode(data)
		open(filename, "w").write(jstr)
		return self
	
	
	def load(self, filename):
		""" Loads profile from file. Returns self """
		data = json.loads(open(filename, "r").read())
		# Buttons
		self.buttons = {}
		for x in SCButtons:
			self.buttons[x] = self.parser.from_json_data(data["buttons"], x.name)
		
		# Stick & gyro
		self.stick = self.parser.from_json_data(data, "stick")
		self.gyro = self.parser.from_json_data(data, "gyro")
		
		if "triggers" in data:
			# Old format
			# Triggers
			self.triggers = ({
				x : self.parser.from_json_data(data["triggers"], x) for x in Profile.TRIGGERS
			})
			
			# Pads
			self.pads = {
				Profile.LEFT	: self.parser.from_json_data(data, "left_pad"),
				Profile.RIGHT	: self.parser.from_json_data(data, "right_pad"),
			}
		else:
			# New format
			# Triggers
			self.triggers = {
				Profile.LEFT	: self.parser.from_json_data(data, "trigger_left"),
				Profile.RIGHT	: self.parser.from_json_data(data, "trigger_right"),
			}
		
			# Pads
			self.pads = {
				Profile.LEFT	: self.parser.from_json_data(data, "pad_left"),
				Profile.RIGHT	: self.parser.from_json_data(data, "pad_right"),
			}
		
		# Menus
		self.menus = {}
		if "menus" in data:
			for id in data["menus"]:
				for invalid_char in ".:/":
					if invalid_char in id:
						raise ValueError("Invalid character '%s' in menu id '%s'" % (invalid_char, id))
				self.menus[id] = MenuData.from_json_data(data["menus"][id], self.parser)
		
		return self
	
	
	def compress(self):
		"""
		Calls compress on every action to throw out some redundant stuff.
		Note that calling save() after compress() will break stuff.
		"""
		for dct in (self.buttons, self.triggers, self.pads):
			for x in dct:
				dct[x] = dct[x].compress()
		self.stick = self.stick.compress()
		self.gyro = self.gyro.compress()
Ejemplo n.º 49
0
	def on_btClearCircularAxis_clicked(self, *a):
		btCircularAxis = self.builder.get_object("btCircularAxis")
		self.circular_axis = NoAction()
		btCircularAxis.set_label(self.circular_axis.describe(Action.AC_PAD))
		self.editor.set_action(self.make_circular_action())
Ejemplo n.º 50
0
class Profile(object):
	VERSION = 1.1	# Current profile version. When loading profile file
					# with version lower than this, auto-conversion may happen
	
	LEFT  = LEFT
	RIGHT = RIGHT
	WHOLE = WHOLE
	STICK = STICK
	GYRO  = GYRO
	X, Y, Z = "X", "Y", "Z"
	STICK_AXES = { X : "lpad_x", Y : "lpad_y" }
	LPAD_AXES  = STICK_AXES
	RPAD_AXES  = { X : "rpad_x", Y : "rpad_y" }
	TRIGGERS   = [ LEFT, RIGHT ]
	
	def __init__(self, parser):
		self.parser = parser
		self.buttons = { x : NoAction() for x in SCButtons }
		self.menus = {}
		self.stick = NoAction()
		self.triggers = { Profile.LEFT : NoAction(), Profile.RIGHT : NoAction() }
		self.pads = { Profile.LEFT : NoAction(), Profile.RIGHT : NoAction() }
		self.gyro = NoAction()
	
	
	def save(self, filename):
		""" Saves profile into file. Returns self """
		data = {
			'buttons'		: {},
			'stick'			: self.stick,
			'gyro'			: self.gyro,
			'trigger_left'	: self.triggers[Profile.LEFT],
			'trigger_right'	: self.triggers[Profile.RIGHT],
			"pad_left"		: self.pads[Profile.LEFT],
			"pad_right"		: self.pads[Profile.RIGHT],
			"menus"			: { id : self.menus[id].encode() for id in self.menus },
			"version"		: Profile.VERSION
		}
		
		for i in self.buttons:
			if self.buttons[i]:
				data['buttons'][i.name] = self.buttons[i]
		
		# Generate & save json
		jstr = Encoder(sort_keys=True, indent=4).encode(data)
		open(filename, "w").write(jstr)
		return self
	
	
	def load(self, filename):
		""" Loads profile from file. Returns self """
		data = json.loads(open(filename, "r").read())
		# Version
		try:
			version = int(data["version"])
		except:
			version = 0
		
		# Buttons
		self.buttons = {}
		for x in SCButtons:
			self.buttons[x] = self.parser.from_json_data(data["buttons"], x.name)
		
		# Stick & gyro
		self.stick = self.parser.from_json_data(data, "stick")
		self.gyro = self.parser.from_json_data(data, "gyro")
		
		if "triggers" in data:
			# Old format
			# Triggers
			self.triggers = ({
				x : self.parser.from_json_data(data["triggers"], x) for x in Profile.TRIGGERS
			})
			
			# Pads
			self.pads = {
				Profile.LEFT	: self.parser.from_json_data(data, "left_pad"),
				Profile.RIGHT	: self.parser.from_json_data(data, "right_pad"),
			}
		else:
			# New format
			# Triggers
			self.triggers = {
				Profile.LEFT	: self.parser.from_json_data(data, "trigger_left"),
				Profile.RIGHT	: self.parser.from_json_data(data, "trigger_right"),
			}
		
			# Pads
			self.pads = {
				Profile.LEFT	: self.parser.from_json_data(data, "pad_left"),
				Profile.RIGHT	: self.parser.from_json_data(data, "pad_right"),
			}
		
		# Menus
		self.menus = {}
		if "menus" in data:
			for id in data["menus"]:
				for invalid_char in ".:/":
					if invalid_char in id:
						raise ValueError("Invalid character '%s' in menu id '%s'" % (invalid_char, id))
				self.menus[id] = MenuData.from_json_data(data["menus"][id], self.parser)
		
		# Conversion
		if version < Profile.VERSION:
			self._convert(version)
		
		return self
	
	
	def compress(self):
		"""
		Calls compress on every action to throw out some redundant stuff.
		Note that calling save() after compress() will break stuff.
		"""
		for dct in (self.buttons, self.triggers, self.pads):
			for x in dct:
				dct[x] = dct[x].compress()
		self.stick = self.stick.compress()
		self.gyro = self.gyro.compress()
	
	
	def _convert(self, from_version):
		""" Performs conversion from older profile version """
		if from_version < 1:
			from scc.modifiers import ModeModifier, HoldModifier
			from scc.special_actions import MenuAction
			# Add 'display Default.menu if center button is held' for old profiles
			c = self.buttons[SCButtons.C]
			if not c:
				# Nothing set to C button
				self.buttons[SCButtons.C] = HoldModifier(
					MenuAction("Default.menu"),
					normalaction = MenuAction("Default.menu")
				)
			elif hasattr(c, "holdaction") and c.holdaction:
				# Already set to something, don't overwrite it
				pass
			elif c.to_string().startswith("OSK."):
				# Special case, don't touch this either
				pass
			else:
				self.buttons[SCButtons.C] = HoldModifier(
					MenuAction("Default.menu"),
					normalaction = self.buttons[SCButtons.C]
				)
		if from_version < 1.1:
			# Convert old scrolling wheel to new representation
			from scc.modifiers import FeedbackModifier, BallModifier
			from scc.actions import MouseAction, XYAction
			from scc.uinput import Rels
			iswheelaction = ( lambda x : isinstance(x, MouseAction) and
					x.parameters[0] in (Rels.REL_HWHEEL, Rels.REL_WHEEL) )
			for p in (Profile.LEFT, Profile.RIGHT):
				a, feedback = self.pads[p], None
				if isinstance(a, FeedbackModifier):
					feedback = a.haptic.get_position()
					a = a.action
				if isinstance(a, XYAction):
					if iswheelaction(a.x) or iswheelaction(a.y):
						n = BallModifier(XYAction(a.x, a.y))
						if feedback:
							n = FeedbackModifier(feedback, 4096, 16, n)
						self.pads[p] = n
						log.info("Converted %s to %s", a.to_string(), n.to_string())
Ejemplo n.º 51
0
class DoubleclickModifier(Modifier):
	COMMAND = "doubleclick"
	DEAFAULT_TIMEOUT = 0.2
	TIMEOUT_KEY = "time"
	PROFILE_KEY_PRIORITY = 3
	
	def __init__(self, doubleclickaction, normalaction=None, time=None):
		Modifier.__init__(self)
		self.action = doubleclickaction
		self.normalaction = normalaction or NoAction()
		self.holdaction = NoAction()
		self.timeout = time or DoubleclickModifier.DEAFAULT_TIMEOUT
		self.waiting = False
		self.pressed = False
		self.active = None
	
	
	def encode(self):
		if self.normalaction:
			rv = self.normalaction.encode()
		else:
			rv = {}
		rv[DoubleclickModifier.COMMAND] = self.action.encode()
		if self.holdaction:
			rv[HoldModifier.COMMAND] = self.holdaction.encode()
		if self.timeout != DoubleclickModifier.DEAFAULT_TIMEOUT:
			rv[DoubleclickModifier.TIMEOUT_KEY] = self.timeout
		if self.name:
			rv[NameModifier.COMMAND] = self.name
		return rv
	
	
	@staticmethod
	def decode(data, a, parser, *b):
		args = [ parser.from_json_data(data[DoubleclickModifier.COMMAND]), a ]
		a = DoubleclickModifier(*args)
		if DoubleclickModifier.TIMEOUT_KEY in data:
			a.timeout = data[DoubleclickModifier.TIMEOUT_KEY]
		return a
	
	
	def strip(self):
		if self.holdaction:
			return self.holdaction.strip()
		return self.action.strip()
	
	
	def compress(self):
		self.action = self.action.compress()
		self.holdaction = self.holdaction.compress()
		self.normalaction = self.normalaction.compress()
		
		if isinstance(self.normalaction, DoubleclickModifier):
			self.action = self.action.compress() or self.normalaction.action.compress()
			self.holdaction = self.holdaction.compress() or self.normalaction.holdaction.compress()
			self.normalaction = self.normalaction.normalaction.compress()
		elif isinstance(self.action, HoldModifier):
			self.holdaction = self.action.holdaction.compress()
			self.action = self.action.normalaction.compress()
		elif isinstance(self.holdaction, DoubleclickModifier):
			self.action = self.holdaction.action.compress()
			self.holdaction = self.holdaction.normalaction.compress()
		elif isinstance(self.holdaction, DoubleclickModifier):
			self.action = self.action.compress() or self.holdaction.action.compress()
			self.normalaction = self.normalaction.compress() or self.holdaction.normalaction.compress()
			self.holdaction = self.holdaction.holdaction.compress()
		return self
	
	
	def __str__(self):
		l = [ self.action ]
		if self.normalaction:
			l += [ self.normalaction ]
		return "<Modifier %s dbl='%s' hold='%s' normal='%s'>" % (
			self.COMMAND, self.action, self.holdaction, self.normalaction )
	
	__repr__ = __str__
	
	
	def describe(self, context):
		l = [ ]
		if self.action:
			l += [ self.action ]
		if self.holdaction:
			l += [ self.holdaction ]
		if self.normalaction:
			l += [ self.normalaction ]
		return "\n".join([ x.describe(context) for x in l ])
	
	
	def to_string(self, multiline=False, pad=0):
		return self._mod_to_string(Action.strip_defaults(self), multiline, pad)
	
	
	def button_press(self, mapper):
		self.pressed = True
		if self.waiting:
			# Double-click happened
			mapper.remove_scheduled(self.on_timeout)
			self.waiting = False
			self.active = self.action
			self.active.button_press(mapper)
		else:
			# First click, start the timer
			self.waiting = True
			mapper.schedule(self.timeout, self.on_timeout)
	
	
	def button_release(self, mapper):
		self.pressed = False
		if self.waiting and self.active is None and not self.action:
			# In HoldModifier, button released before timeout
			mapper.remove_scheduled(self.on_timeout)
			self.waiting = False
			if self.normalaction:
				self.normalaction.button_press(mapper)
				self.normalaction.button_release(mapper)
		elif self.active:
			# Released held button
			self.active.button_release(mapper)
			self.active = None
	
	
	def on_timeout(self, mapper, *a):
		if self.waiting:
			self.waiting = False
			if self.pressed:
				# Timeouted while button is still pressed
				self.active = self.holdaction if self.holdaction else self.normalaction
				self.active.button_press(mapper)
			elif self.normalaction:
				# User did short click and nothing else
				self.normalaction.button_press(mapper)
				self.normalaction.button_release(mapper)
Ejemplo n.º 52
0
class DoubleclickModifier(Modifier):
	COMMAND = "doubleclick"
	DEAFAULT_TIMEOUT = 0.2
	
	def __init__(self, doubleclickaction, normalaction=None):
		Modifier.__init__(self)
		self.action = doubleclickaction
		self.normalaction = normalaction or NoAction()
		self.holdaction = NoAction()
		self.timeout = self.DEAFAULT_TIMEOUT
		self.waiting = False
		self.pressed = False
		self.active = None
	
	
	def set_haptic(self, hapticdata):
		supports = self.action.set_haptic(hapticdata)
		if self.normalaction:
			supports = self.normalaction.set_haptic(hapticdata) or supports
		if self.holdaction:
			supports = self.holdaction.set_haptic(hapticdata) or supports
		return supports
	
	
	def set_speed(self, x, y, z):
		supports = self.action.set_speed(x, y, z)
		if self.normalaction:
			supports = self.normalaction.set_speed(x, y, z) or supports
		if self.holdaction:
			supports = self.holdaction.set_speed(x, y, z) or supports
		return supports
	
	
	def strip(self):
		if self.holdaction:
			return self.holdaction.strip()
		return self.action.strip()
	
	
	def compress(self):
		self.action = self.action.compress()
		self.holdaction = self.holdaction.compress()
		self.normalaction = self.normalaction.compress()
		
		if isinstance(self.normalaction, DoubleclickModifier):
			self.action = self.action.compress() or self.normalaction.action.compress()
			self.holdaction = self.holdaction.compress() or self.normalaction.holdaction.compress()
			self.normalaction = self.normalaction.normalaction.compress()
		elif isinstance(self.action, HoldModifier):
			self.holdaction = self.action.holdaction.compress()
			self.action = self.action.normalaction.compress()
		elif isinstance(self.holdaction, DoubleclickModifier):
			self.action = self.holdaction.action.compress()
			self.holdaction = self.holdaction.normalaction.compress()
		elif isinstance(self.holdaction, DoubleclickModifier):
			self.action = self.action.compress() or self.holdaction.action.compress()
			self.normalaction = self.normalaction.compress() or self.holdaction.normalaction.compress()
			self.holdaction = self.holdaction.holdaction.compress()
		return self
	
	
	def __str__(self):
		l = [ self.action ]
		if self.normalaction:
			l += [ self.normalaction ]
		return "<Modifier %s dbl='%s' hold='%s' normal='%s'>" % (
			self.COMMAND, self.action, self.holdaction, self.normalaction )
	
	__repr__ = __str__
	
	
	def describe(self, context):
		l = [ ]
		if self.action:
			l += [ self.action ]
		if self.holdaction:
			l += [ self.holdaction ]
		if self.normalaction:
			l += [ self.normalaction ]
		return "\n".join([ x.describe(context) for x in l ])
	
	
	def to_string(self, multiline=False, pad=0):
		firstline, lastline = "", ""
		if self.action:
			firstline += DoubleclickModifier.COMMAND + "(" + self.action.to_string() + ","
			lastline += ")"
		if self.holdaction:
			firstline += HoldModifier.COMMAND + "(" + self.holdaction.to_string() + ","
			lastline += ")"
		
		if multiline:
			if self.normalaction:
				rv = [ (" " * pad) + firstline ]
				rv += self.normalaction.to_string(True, pad+2).split("\n")
				rv += [ (" " * pad) + lastline ]
			else:
				rv = [ firstline.strip(",") + lastline ]
			return "\n".join(rv)
		elif self.normalaction:
			return firstline + self.normalaction.to_string() + lastline
		else:
			return firstline.strip(",") + lastline
	
	
	def encode(self):
		if self.normalaction:
			rv = self.normalaction.encode()
		else:
			rv = {}
		rv['doubleclick'] = self.action.encode()
		if self.holdaction:
			rv['hold'] = self.holdaction.encode()
		if self.name: rv['name'] = self.name
		return rv
	
	
	def button_press(self, mapper):
		self.pressed = True
		if self.waiting:
			# Double-click happened
			mapper.remove_scheduled(self.on_timeout)
			self.waiting = False
			self.active = self.action
			self.active.button_press(mapper)
		else:
			# First click, start the timer
			self.waiting = True
			mapper.schedule(self.timeout, self.on_timeout)
	
	
	def button_release(self, mapper):
		self.pressed = False
		if self.waiting and self.active is None and not self.action:
			# In HoldModifier, button released before timeout
			mapper.remove_scheduled(self.on_timeout)
			self.waiting = False
			if self.normalaction:
				self.normalaction.button_press(mapper)
				self.normalaction.button_release(mapper)
		elif self.active:
			# Released held button
			self.active.button_release(mapper)
			self.active = None
	
	
	def on_timeout(self, mapper, *a):
		if self.waiting:
			self.waiting = False
			if self.pressed:
				# Timeouted while button is still pressed
				self.active = self.holdaction if self.holdaction else self.normalaction
				self.active.button_press(mapper)
			elif self.normalaction:
				# User did short click and nothing else
				self.normalaction.button_press(mapper)
				self.normalaction.button_release(mapper)
Ejemplo n.º 53
0
class AxisActionComponent(AEComponent, TimerManager):
	GLADE = "ae/axis_action.glade"
	NAME = "axis_action"
	CTXS = Action.AC_STICK | Action.AC_PAD
	PRIORITY = 3
	
	def __init__(self, app, editor):
		AEComponent.__init__(self, app, editor)
		TimerManager.__init__(self)
		self._recursing = False
		self.relative_area = False
		self.osd_area_instance = None
		self.on_wayland = False
		self.circular_axis = MouseAction(Rels.REL_WHEEL)
		self.circular_buttons = [ None, None ]
		self.button = None
		self.parser = GuiActionParser()
	
	
	def load(self):
		if self.loaded : return
		AEComponent.load(self)
		cbAreaType = self.builder.get_object("cbAreaType")
		cbAreaType.set_row_separator_func( lambda model, iter : model.get_value(iter, 0) == "-" )
		self.on_wayland = "WAYLAND_DISPLAY" in os.environ or not isinstance(Gdk.Display.get_default(), GdkX11.X11Display)
		if self.on_wayland:
			self.builder.get_object("lblArea").set_text(_("Note: Mouse Region option is not available with Wayland-based display server"))
			self.builder.get_object("grArea").set_sensitive(False)
		
		# Remove options that are not applicable to currently editted input
		if self.editor.get_id() in STICKS:
			# Remove "Mouse Region", "Mouse" and "Mouse (Emulate Stick)" options
			# when editing stick bindings
			cb = self.builder.get_object("cbAxisOutput")
			for row in cb.get_model():
				if row[2] in ("wheel_pad", "area", "mouse", "mouse_pad"):
					cb.get_model().remove(row.iter)
		else:
			# Remove "Mouse" option when editing pads
			# (it's effectivelly same as Trackpad)
			cb = self.builder.get_object("cbAxisOutput")
			for row in cb.get_model():
				if row[2] in ("wheel_stick", "mouse_stick"):
					cb.get_model().remove(row.iter)
	
	
	def hidden(self):
		self.update_osd_area(None)
	
	
	def set_action(self, mode, action):
		if self.handles(mode, action):
			cb = self.builder.get_object("cbAxisOutput")
			if isinstance(action, AreaAction):
				self.load_area_action(action)
				self.set_cb(cb, "area", 2)
				self.update_osd_area(action)
				return
			self.update_osd_area(None)
			if isinstance(action, MouseAction):
				self.load_mouse_action(action)
			elif isinstance(action, CircularModifier):
				self.load_circular_action(action)
			elif isinstance(action, ButtonAction):
				self.load_button_action(action)
			elif isinstance(action, XYAction):
				if self.editor.friction > 0:
					self.load_mouse_action(action)
				else:
					p = [ None, None ]
					for x in (0, 1):
						if len(action.actions[0].strip().parameters) >= x:
							if len(action.actions[x].strip().parameters) > 0:
								p[x] = action.actions[x].strip().parameters[0]
					if p[0] == Axes.ABS_X and p[1] == Axes.ABS_Y:
						self.set_cb(cb, "lstick", 2)
					elif p[0] == Axes.ABS_RX and p[1] == Axes.ABS_RY:
						if isinstance(action, RelXYAction):
							self.set_cb(cb, "rstick_rel", 2)
						else:
							self.set_cb(cb, "rstick", 2)
					elif p[0] == Rels.REL_HWHEEL and p[1] == Rels.REL_WHEEL:
						self.set_cb(cb, "wheel_pad", 2)
						self.set_cb(cb, "wheel_stick", 2)
			else:
				self.set_cb(cb, "none", 2)
	
	
	def update_osd_area(self, action):
		""" Updates preview area displayed on screen """
		if action:
			if self.osd_area_instance is None:
				if self.on_wayland:
					# Cannot display preview with non-X11 backends
					return
				self.osd_area_instance = Area()
				self.osd_area_instance.show()
			action.update_osd_area(self.osd_area_instance, FakeMapper(self.editor))
			self.timer("area", 0.5, self.update_osd_area, action)
		elif self.osd_area_instance:
			self.osd_area_instance.quit()
			self.osd_area_instance = None
			self.cancel_timer("area")
	
	
	def load_circular_action(self, action):
		cbAxisOutput = self.builder.get_object("cbAxisOutput")
		btCircularAxis = self.builder.get_object("btCircularAxis")
		btCircularButton0 = self.builder.get_object("btCircularButton0")
		btCircularButton1 = self.builder.get_object("btCircularButton1")
		
		# Turn action into list of subactions (even if it's just single action)
		if isinstance(action.action, MultiAction):
			actions = action.action.actions
		else:
			actions = [ action.action ]
		
		# Parse that list
		self.circular_axis, self.circular_buttons = NoAction(), [ None, None ]
		for action in actions:
			if isinstance(action, ButtonAction):
				self.circular_buttons = [ action.button, action.button2 ]
			else:
				self.circular_axis = action
		
		# Set labels
		b0, b1 = self.circular_buttons
		btCircularButton0.set_label(ButtonAction.describe_button(b0))
		btCircularButton1.set_label(ButtonAction.describe_button(b1))
		btCircularAxis.set_label(self.circular_axis.describe(Action.AC_PAD))
		
		self.set_cb(cbAxisOutput, "circular", 2)
	
	
	def load_button_action(self, action):
		self.button = action
		cbAxisOutput = self.builder.get_object("cbAxisOutput")
		btSingleButton = self.builder.get_object("btSingleButton")
		btSingleButton.set_label(self.button.describe(Action.AC_PAD))
		self.set_cb(cbAxisOutput, "button", 2)
	
	
	def load_mouse_action(self, action):
		cbMouseOutput = self.builder.get_object("cbMouseOutput")
		cbAxisOutput = self.builder.get_object("cbAxisOutput")
		self._recursing = True
		if isinstance(action, MouseAction):
			self.set_cb(cbMouseOutput, "mouse", 1)
			self.set_cb(cbAxisOutput, "mouse", 2)
		elif isinstance(action, XYAction):
			if isinstance(action.x, AxisAction):
				if action.x.parameters[0] == Axes.ABS_X:
					self.set_cb(cbMouseOutput, "left", 1)
				else:
					self.set_cb(cbMouseOutput, "right", 1)
				self.set_cb(cbAxisOutput, "mouse", 2)
			elif isinstance(action.x, MouseAction):
				if self.editor.get_id() in STICKS:
					self.set_cb(cbAxisOutput, "wheel_stick", 2)
				else:
					self.set_cb(cbAxisOutput, "wheel_pad", 2)
		self._recursing = False
	
	
	def load_area_action(self, action):
		"""
		Load AreaAction values into UI.
		"""
		cbAreaType = self.builder.get_object("cbAreaType")
		
		x1, y1, x2, y2 = action.coords
		self.relative_area = False
		if isinstance(action, RelAreaAction):
			key = "screensize"
			self.relative_area = True
			x1, y1, x2, y2 = x1 * 100.0, y1 * 100.0, x2 * 100.0, y2 * 100.0
		elif isinstance(action, RelWinAreaAction):
			key = "windowsize"
			self.relative_area = True
			x1, y1, x2, y2 = x1 * 100.0, y1 * 100.0, x2 * 100.0, y2 * 100.0
		else:
			t1 = "1" if x1 < 0 and x2 < 0 else "0"
			t2 = "1" if y1 < 0 and y2 < 0 else "0"
			x1, y1, x2, y2 = abs(x1), abs(y1), abs(x2), abs(y2)
			if x2 < x1 : x1, x2 = x2, x1
			if y2 < y1 : y1, y2 = y2, y1
			if isinstance(action, WinAreaAction):
				key = "window-%s%s" % (t1, t2)
			else:
				key = "screen-%s%s" % (t1, t2)
		
		self._recursing = True
		self.builder.get_object("sbAreaX1").set_value(x1)
		self.builder.get_object("sbAreaY1").set_value(y1)
		self.builder.get_object("sbAreaX2").set_value(x2)
		self.builder.get_object("sbAreaY2").set_value(y2)
		self.builder.get_object("cbAreaOSDEnabled").set_active(self.editor.osd)
		self.builder.get_object("cbAreaClickEnabled").set_active(self.pressing_pad_clicks())
		for row in cbAreaType.get_model():
			if key == row[1]:
				cbAreaType.set_active_iter(row.iter)
				break
		self._recursing = False
	
	
	def on_btCircularAxis_clicked(self, *a):
		def cb(action):
			self.circular_axis = action
			btCircularAxis = self.builder.get_object("btCircularAxis")
			btCircularAxis.set_label(action.describe(Action.AC_PAD))
			self.editor.set_action(self.make_circular_action())
		
		b = SimpleChooser(self.app, "axis", cb)
		b.set_title(_("Select Axis"))
		b.display_action(Action.AC_STICK, self.circular_axis)
		b.show(self.editor.window)
	
	
	def on_btCircularButton_clicked(self, button, *a):
		index = 0 if button == self.builder.get_object("btCircularButton0") else 1
		def cb(action):
			self.circular_buttons[index] = action.button
			btCircularButton = self.builder.get_object("btCircularButton%s" % (index, ))
			btCircularButton.set_label(action.describe(Action.AC_PAD))
			self.editor.set_action(self.make_circular_action())
		
		b = SimpleChooser(self.app, "buttons", cb)
		b.set_title(_("Select Button"))
		b.display_action(Action.AC_STICK, self.circular_axis)
		b.show(self.editor.window)
	
	
	def on_btClearCircularAxis_clicked(self, *a):
		btCircularAxis = self.builder.get_object("btCircularAxis")
		self.circular_axis = NoAction()
		btCircularAxis.set_label(self.circular_axis.describe(Action.AC_PAD))
		self.editor.set_action(self.make_circular_action())
	
	
	def on_btClearCircularButtons_clicked(self, *a):
		btCircularButton0 = self.builder.get_object("btCircularButton0")
		btCircularButton1 = self.builder.get_object("btCircularButton1")
		self.circular_buttons = [ None, None ]
		btCircularButton0.set_label(NoAction().describe(Action.AC_PAD))
		btCircularButton1.set_label(NoAction().describe(Action.AC_PAD))
		self.editor.set_action(self.make_circular_action())
	
	
	def on_btSingleButton_clicked(self, *a):
		def cb(action):
			self.button = action
			btSingleButton = self.builder.get_object("btSingleButton")
			btSingleButton.set_label(self.button.describe(Action.AC_PAD))
			self.editor.set_action(self.button)
		
		b = SimpleChooser(self.app, "buttons", cb)
		b.set_title(_("Select Button"))
		b.display_action(Action.AC_STICK, self.circular_axis)
		b.show(self.editor.window)
	
	
	def on_cbAreaOSDEnabled_toggled(self, *a):
		self.editor.builder.get_object("cbOSD").set_active(
			self.builder.get_object("cbAreaOSDEnabled").get_active())
	
	
	def pressing_pad_clicks(self):
		"""
		Returns True if currently edited pad is set to press left mouse
		button when pressed.
		(yes, this is used somewhere)
		"""
		side = getattr(SCButtons, self.editor.get_id())
		c_action = self.app.current.buttons[side]
		if isinstance(c_action, ButtonAction):
			return c_action.button == Keys.BTN_LEFT
		return False
	
	
	def on_ok(self, action):
		if isinstance(action.strip(), AreaAction):
			# Kinda hacky way to set action on LPAD press or RPAD press
			# when user selects Mouse Area as ouput and checks
			# 'Pressing the Pad Clicks' checkbox
			side = getattr(SCButtons, self.editor.get_id())
			clicks = self.pressing_pad_clicks()
			
			if self.builder.get_object("cbAreaClickEnabled").get_active():
				if not clicks:
					# Turn pad press into mouse clicks
					self.app.set_action(self.app.current, side, ButtonAction(Keys.BTN_LEFT))
			else:
				if clicks:
					# Clear action created above if checkbox is uncheck
					self.app.set_action(self.app.current, side, NoAction())
	
	
	def on_mouse_options_changed(self, *a):
		if self._recursing : return
		action = self.make_mouse_action()
		self.editor.set_action(action)
	
	
	def make_mouse_action(self):
		"""
		Loads values from UI into trackball-related action
		"""
		cbMouseOutput = self.builder.get_object("cbMouseOutput")
		a_str = cbMouseOutput.get_model().get_value(cbMouseOutput.get_active_iter(), 2)
		return self.parser.restart(a_str).parse()
	
	
	def make_circular_action(self):
		"""
		Constructs Circular Modifier
		"""
		if self.circular_axis and any(self.circular_buttons):
			return CircularModifier(MultiAction(
				self.circular_axis, ButtonAction(*self.circular_buttons)))
		elif any(self.circular_buttons):
			return CircularModifier(ButtonAction(*self.circular_buttons))
		else:
			return CircularModifier(self.circular_axis)
	
	
	def make_area_action(self):
		"""
		Loads values from UI into new AreaAction or subclass.
		"""
		# Prepare
		cbAreaType = self.builder.get_object("cbAreaType")
		# Read numbers
		x1 = self.builder.get_object("sbAreaX1").get_value()
		y1 = self.builder.get_object("sbAreaY1").get_value()
		x2 = self.builder.get_object("sbAreaX2").get_value()
		y2 = self.builder.get_object("sbAreaY2").get_value()
		# Determine exact action type by looking into Area Type checkbox
		# (this part may seem little crazy)
		# ... numbers
		key = cbAreaType.get_model().get_value(cbAreaType.get_active_iter(), 1)
		if "-" in key:
			if key[-2] == "1":
				# Before-last character ius "1", that means that X coords are
				# counted from other side and has to be negated
				x1, x2 = -x1, -x2
			if key[-1] == "1":
				# Key ends with "1". Same thing as above but for Y coordinate
				y1, y2 = -y1, -y2
		if "size" in key:
			x1, y1, x2, y2 = x1 / 100.0, y1 / 100.0, x2 / 100.0, y2 / 100.0
		# ... class 
		if "window-" in key:
			cls = WinAreaAction
			self.relative_area = False
		elif "screensize" == key:
			cls = RelAreaAction
			self.relative_area = True
		elif "windowsize" == key:
			cls = RelWinAreaAction
			self.relative_area = True
		else: # "screen" in key
			cls = AreaAction
			self.relative_area = False
		if not self.relative_area:
			x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
			
		return cls(x1, y1, x2, y2)
	
	
	def get_button_title(self):
		return _("Joystick / Mouse")
	
	
	def handles(self, mode, action):
		if isinstance(action, (NoAction, MouseAction, CircularModifier,
					InvalidAction, AreaAction, ButtonAction)):
			return True
		if isinstance(action, BallModifier):
			if isinstance(action.action, XYAction):
				return (
					isinstance(action.action.x, (AxisAction, MouseAction))
					and isinstance(action.action.x, (AxisAction, MouseAction))
				)
			return isinstance(action.action, MouseAction)
		if isinstance(action, XYAction):
			p = [ None, None ]
			for x in (0, 1):
				if len(action.actions[0].strip().parameters) >= x:
					if len(action.actions[x].strip().parameters) > 0:
						p[x] = action.actions[x].strip().parameters[0]
			if p[0] == Axes.ABS_X and p[1] == Axes.ABS_Y:
				return True
			elif p[0] == Axes.ABS_RX and p[1] == Axes.ABS_RY:
				return True
			elif p[0] == Axes.ABS_HAT0X and p[1] == Axes.ABS_HAT0Y:
				return True
			elif p[0] == Rels.REL_HWHEEL and p[1] == Rels.REL_WHEEL:
				return True
		return False
	
	
	def on_area_options_changed(self, *a):
		if self._recursing : return
		action = self.make_area_action()
		self.editor.set_action(action)
		self.update_osd_area(action)
		for x in ('sbAreaX1', 'sbAreaX2', 'sbAreaY1', 'sbAreaY2'):
			spin = self.builder.get_object(x)
			if self.relative_area:
				spin.get_adjustment().set_upper(100)
			else:
				spin.get_adjustment().set_upper(1000)
			self.on_sbArea_output(spin)
	
	
	def on_sbArea_output(self, button, *a):
		if self.relative_area:
			button.set_text("%s %%" % (button.get_value()))
		else:
			button.set_text("%s px" % (int(button.get_value())))
	
	
	def on_sbArea_focus_out_event(self, button, *a):
		GLib.idle_add(self.on_sbArea_output, button)
	
	
	def on_sbArea_changed(self, button, *a):
		self.on_sbArea_output(button)
		self.on_area_options_changed(button)
	
	
	def on_lblSetupTrackball_activate_link(self, trash, link):
		self.editor.on_link(link)
	
	
	def on_cbAxisOutput_changed(self, *a):
		cbAxisOutput = self.builder.get_object("cbAxisOutput")
		stActionData = self.builder.get_object("stActionData")
		key = cbAxisOutput.get_model().get_value(cbAxisOutput.get_active_iter(), 2)
		if key == 'area':
			stActionData.set_visible_child(self.builder.get_object("grArea"))
			action = self.make_area_action()
			self.update_osd_area(action)
		elif key == "button":
			stActionData.set_visible_child(self.builder.get_object("vbButton"))
			self.button = self.button or ButtonAction(Keys.BTN_GAMEPAD)
			action = self.button
		elif key == "circular":
			stActionData.set_visible_child(self.builder.get_object("grCircular"))
			action = self.make_circular_action()
		elif key == 'mouse':
			stActionData.set_visible_child(self.builder.get_object("vbMose"))
			if self.editor.friction == 0:
				# When switching to mouse, enable trackball by default
				self.editor.friction = 10
			action = self.make_mouse_action()
		else:
			stActionData.set_visible_child(self.builder.get_object("nothing"))
			action = cbAxisOutput.get_model().get_value(cbAxisOutput.get_active_iter(), 0)
			action = self.parser.restart(action).parse()
			self.update_osd_area(None)
		
		self.editor.set_action(action)
Ejemplo n.º 54
0
class DoubleclickModifier(Modifier, HapticEnabledAction):
	COMMAND = "doubleclick"
	DEAFAULT_TIMEOUT = 0.2
	TIMEOUT_KEY = "time"
	PROFILE_KEY_PRIORITY = 3
	
	def __init__(self, doubleclickaction, normalaction=None, time=None):
		Modifier.__init__(self)
		HapticEnabledAction.__init__(self)
		self.action = doubleclickaction
		self.normalaction = normalaction or NoAction()
		self.holdaction = NoAction()
		self.actions = ( self.action, self.normalaction, self.holdaction )
		self.timeout = time or DoubleclickModifier.DEAFAULT_TIMEOUT
		self.waiting_task = None
		self.pressed = False
		self.active = None
	
	
	def get_child_actions(self):
		return self.action, self.normalaction, self.holdaction
	
	
	@staticmethod
	def decode(data, a, parser, *b):
		args = [ parser.from_json_data(data[DoubleclickModifier.COMMAND]), a ]
		a = DoubleclickModifier(*args)
		if DoubleclickModifier.TIMEOUT_KEY in data:
			a.timeout = data[DoubleclickModifier.TIMEOUT_KEY]
		return a
	
	
	def strip(self):
		if self.holdaction:
			return self.holdaction.strip()
		return self.action.strip()
	
	
	def compress(self):
		self.action = self.action.compress()
		self.holdaction = self.holdaction.compress()
		self.normalaction = self.normalaction.compress()
		
		for a in (self.holdaction, self.normalaction):
			if isinstance(a, HoldModifier):
				self.holdaction = a.holdaction or self.holdaction
				self.normalaction = a.normalaction or self.normalaction
		
		if isinstance(self.action, HoldModifier):
			self.holdaction = self.action.holdaction
			self.action = self.action.normalaction
		return self
	
	
	def __str__(self):
		l = [ self.action ]
		if self.normalaction:
			l += [ self.normalaction ]
		return "<Modifier %s dbl='%s' hold='%s' normal='%s'>" % (
			self.COMMAND, self.action, self.holdaction, self.normalaction )
	
	__repr__ = __str__
	
	
	def describe(self, context):
		l = [ ]
		if self.action:
			l += [ self.action ]
		if self.holdaction:
			l += [ self.holdaction ]
		if self.normalaction:
			l += [ self.normalaction ]
		return "\n".join([ x.describe(context) for x in l ])
	
	
	def to_string(self, multiline=False, pad=0):
		timeout = ""
		if DoubleclickModifier.DEAFAULT_TIMEOUT != self.timeout:
			timeout = ", %s" % (self.timeout)
		if self.action and self.normalaction and self.holdaction:
			return "doubleclick(%s, hold(%s, %s)%s)" % (
				NameModifier.unstrip(self.action).to_string(multiline, pad),
				NameModifier.unstrip(self.holdaction).to_string(multiline, pad),
				NameModifier.unstrip(self.normalaction).to_string(multiline, pad),
				timeout
			)
		elif self.action and self.normalaction and not self.holdaction:
			return "doubleclick(%s, %s%s)" % (
				NameModifier.unstrip(self.action).to_string(multiline, pad),
				NameModifier.unstrip(self.normalaction).to_string(multiline, pad),
				timeout
			)
		elif not self.action and self.normalaction and self.holdaction:
			return "hold(%s, %s%s)" % (
				NameModifier.unstrip(self.holdaction).to_string(multiline, pad),
				NameModifier.unstrip(self.normalaction).to_string(multiline, pad),
				timeout
			)
		elif not self.action and not self.normalaction and self.holdaction:
			return "hold(None, %s%s)" % (
				NameModifier.unstrip(self.holdaction).to_string(multiline, pad),
				timeout
			)
		elif self.action and not self.normalaction and not self.holdaction:
			return "doubleclick(None, %s%s)" % (
				NameModifier.unstrip(self.action).to_string(multiline, pad),
				timeout
			)
		return NameModifier.unstrip(
				self.action or self.normalaction or self.holdaction
			).to_string(multiline, pad)
	
	
	def button_press(self, mapper):
		self.pressed = True
		if self.waiting_task:
			# Double-click happened
			mapper.cancel_task(self.waiting_task)
			self.waiting_task = None
			self.active = self.action
			self.active.button_press(mapper)
		else:
			# First click, start the timer
			self.waiting_task = mapper.schedule(self.timeout, self.on_timeout)
	
	
	def button_release(self, mapper):
		self.pressed = False
		if self.waiting_task and self.active is None and not self.action:
			# In HoldModifier, button released before timeout
			mapper.cancel_task(self.waiting_task)
			self.waiting_task = None
			if self.normalaction:
				self.normalaction.button_press(mapper)
				mapper.schedule(0.02, self.normalaction.button_release)
		elif self.active:
			# Released held button
			self.active.button_release(mapper)
			self.active = None
	
	
	def on_timeout(self, mapper, *a):
		if self.waiting_task:
			self.waiting_task = None
			if self.pressed:
				# Timeouted while button is still pressed
				self.active = self.holdaction if self.holdaction else self.normalaction
				if self.haptic:
					mapper.send_feedback(self.haptic)
				self.active.button_press(mapper)
			elif self.normalaction:
				# User did short click and nothing else
				self.normalaction.button_press(mapper)
				mapper.schedule(0.02, self.normalaction.button_release)
Ejemplo n.º 55
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)
Ejemplo n.º 56
0
class ModeModifier(Modifier):
	COMMAND = "mode"
	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
	
	def __init__(self, *stuff):
		Modifier.__init__(self)
		self.default = None
		self.mods = {}
		self.held_buttons = set()
		self.held_sticks = set()
		self.held_triggers = {}
		self.order = []
		self.old_gyro = None
		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):
				if button is None:
					self.default = i
					continue
				self.mods[button] = i
				self.order.append(button)
				button = None
			elif i in SCButtons:
				button = i
			else:
				raise ValueError("Invalid parameter for 'mode': %s" % (i,))
		if self.default is None:
			self.default = NoAction()
	
	
	def set_haptic(self, hapticdata):
		supports = False
		if self.default:
			supports = self.default.set_haptic(hapticdata) or supports
		for a in self.mods.values():
			supports = a.set_haptic(hapticdata) or supports
		return supports
	
	
	def set_speed(self, x, y, z):
		supports = False
		if self.default:
			supports = self.default.set_speed(x, y, z) or supports
		for a in self.mods.values():
			supports = a.set_speed(x, y, z) or supports
		return supports
	
	
	def strip(self):
		# Returns default action or action assigned to first modifier
		if self.default:
			return self.default.strip()
		if len(self.order) > 0:
			return self.mods[self.order[0]].strip()
		# Empty ModeModifier
		return NoAction()
	
	
	def compress(self):
		if self.default:
			self.default = self.default.compress()
		for button in self.mods:
			self.mods[button] = self.mods[button].compress()
		return self
	
	
	def __str__(self):
		rv = [ ]
		for key in self.mods:
			rv += [ key.name, self.mods[key] ]
		if self.default is not None:
			rv += [ self.default ]
		return "<Modifier '%s', %s>" % (self.COMMAND, rv)
	
	__repr__ = __str__
	
	
	def describe(self, context):
		l = []
		if self.default : l.append(self.default)
		for x in self.order:
			l.append(self.mods[x])
		return "\n".join([ x.describe(context) for x in l ])
	
	
	def to_string(self, multiline=False, pad=0):
		if multiline:
			rv = [ (" " * pad) + "mode(" ]
			for key in self.mods:
				a_str = self.mods[key].to_string(True).split("\n")
				a_str[0] = (" " * pad) + "  " + (key.name + ",").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  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 key in self.mods:
				rv += [ key.name, self.mods[key].to_string(False) ]
			if self.default is not None:
				rv += [ self.default.to_string(False) ]
			return "mode(" + ", ".join(rv) + ")"
	
	
	def encode(self):
		rv = self.default.encode()
		rv['modes'] = {}
		for key in self.mods:
			rv['modes'][key.name] = self.mods[key].encode()
		if self.name: rv['name'] = self.name
		return rv
	
	
	def select(self, mapper):
		"""
		Selects action by pressed button.
		"""
		for b in self.order:
			if mapper.is_pressed(b):
				return self.mods[b]
		return self.default
	
	
	def select_b(self, mapper):
		"""
		Same as select but returns button as well.
		"""
		for b in self.order:
			if mapper.is_pressed(b):
				return b, self.mods[b]
		return None, self.default
	
	
	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_gyro:
			if self.old_gyro:
				self.old_gyro.gyro(mapper, 0, 0, 0, *q)
			self.old_gyro = 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 b in self.held_sticks:
					b.whole(mapper, 0, 0, what)
				self.held_sticks.clear()
			else:
				self.held_sticks.add(self.select(mapper))
				for b in self.held_sticks:
					b.whole(mapper, x, y, what)
		else:
			return self.select(mapper).whole(mapper, x, y, what)
Ejemplo n.º 57
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 = {}
		self.held_buttons = set()
		self.held_sticks = set()
		self.held_triggers = {}
		self.order = []
		self.old_gyro = 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):
				if button is None:
					self.default = i
					continue
				self.mods[button] = i
				self.order.append(button)
				button = None
			elif i in SCButtons:
				button = i
			else:
				raise ValueError("Invalid parameter for 'mode': %s" % (i,))
		if self.default is None:
			self.default = NoAction()
	
	
	def encode(self):
		rv = self.default.encode()
		modes = {}
		for key in self.mods:
			modes[key.name] = self.mods[key].encode()
		rv[ModeModifier.PROFILE_KEYS[0]] = modes
		if self.name:
			rv[NameModifier.COMMAND] = self.name
		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 strip(self):
		# Returns default action or action assigned to first modifier
		if self.default:
			return self.default.strip()
		if len(self.order) > 0:
			return self.mods[self.order[0]].strip()
		# Empty ModeModifier
		return NoAction()
	
	
	def compress(self):
		if self.default:
			self.default = self.default.compress()
		for button in self.mods:
			self.mods[button] = self.mods[button].compress()
		return self
	
	
	def __str__(self):
		rv = [ ]
		for key in self.mods:
			rv += [ key.name, self.mods[key] ]
		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 x in self.order:
			l.append(self.mods[x])
		return "\n".join([ x.describe(context) for x in l ])
	
	
	def to_string(self, multiline=False, pad=0):
		if multiline:
			rv = [ (" " * pad) + "mode(" ]
			for key in self.mods:
				a_str = self.mods[key].to_string(True).split("\n")
				a_str[0] = (" " * pad) + "  " + (key.name + ",").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  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 key in self.mods:
				rv += [ key.name, self.mods[key].to_string(False) ]
			if self.default is not None:
				rv += [ self.default.to_string(False) ]
			return "mode(" + ", ".join(rv) + ")"
	
	
	def select(self, mapper):
		"""
		Selects action by pressed button.
		"""
		for b in self.order:
			if mapper.is_pressed(b):
				return self.mods[b]
		return self.default
	
	
	def select_b(self, mapper):
		"""
		Same as select but returns button as well.
		"""
		for b in self.order:
			if mapper.is_pressed(b):
				return b, self.mods[b]
		return None, self.default
	
	
	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_gyro:
			if self.old_gyro:
				self.old_gyro.gyro(mapper, 0, 0, 0, *q)
			self.old_gyro = 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 b in self.held_sticks:
					b.whole(mapper, 0, 0, what)
				self.held_sticks.clear()
			else:
				self.held_sticks.add(self.select(mapper))
				for b in self.held_sticks:
					b.whole(mapper, x, y, what)
		else:
			return self.select(mapper).whole(mapper, x, y, what)
Ejemplo n.º 58
0
class Profile(object):
	VERSION = 1.4	# Current profile version. When loading profile file
					# with version lower than this, auto-conversion may happen
	
	LEFT  = LEFT
	RIGHT = RIGHT
	LPAD = SCButtons.LPAD.name
	RPAD = SCButtons.RPAD.name
	CPAD = CPAD
	WHOLE = WHOLE
	STICK = STICK
	GYRO  = GYRO
	X, Y, Z = "X", "Y", "Z"
	STICK_AXES = { X : "lpad_x", Y : "lpad_y" }
	LPAD_AXES  = STICK_AXES
	RPAD_AXES  = { X : "rpad_x", Y : "rpad_y" }
	TRIGGERS   = [ LEFT, RIGHT ]
	
	def __init__(self, parser):
		self.parser = parser
		self.clear()
		self.filename = None
		# UI-only values
		self.is_template = False
		self.description = ""
	
	
	def save(self, filename):
		""" Saves profile into file. Returns self """
		fileobj = file(filename, "w")
		self.save_fileobj(fileobj)
		fileobj.close()
		return self
	
	
	def save_fileobj(self, fileobj):
		""" Saves profile into file-like object. Returns self """
		data = {
			"_"				: (self.description if "\n" not in self.description
								else self.description.strip("\n").split("\n")),
			'buttons'		: {},
			'stick'			: self.stick,
			'gyro'			: self.gyro,
			'trigger_left'	: self.triggers[Profile.LEFT],
			'trigger_right'	: self.triggers[Profile.RIGHT],
			"pad_left"		: self.pads[Profile.LEFT],
			"pad_right"		: self.pads[Profile.RIGHT],
			"cpad"			: self.pads[Profile.CPAD],
			"menus"			: { id : self.menus[id].encode() for id in self.menus },
			"is_template"	: self.is_template,
			"version"		: Profile.VERSION,
		}
		
		for i in self.buttons:
			if self.buttons[i]:
				data['buttons'][i.name] = self.buttons[i]
		
		# Generate & save json
		jstr = Encoder(sort_keys=True, indent=4).encode(data)
		fileobj.write(jstr)
		return self
	
	
	def load(self, filename):
		""" Loads profile from file. Returns self """
		fileobj = open(filename, "r")
		self.load_fileobj(fileobj)
		self.filename = filename
		return self
	
	
	def load_fileobj(self, fileobj):
		"""
		Loads profile from file-like object.
		Filename attribute is not set, what may cause some trouble if used in GUI.
		
		Returns self.
		"""
		data = json.loads(fileobj.read())
		# Version
		try:
			version = float(data["version"])
		except:
			version = 0
		
		# Settings - Description
		# (stored in key "_", so it's serialized on top of JSON file)
		if "_" not in data:
			self.description = ""
		elif type(data["_"]) == list:
			self.description = "\n".join(data["_"])
		else:
			self.description = data["_"]
		# Settings - Template
		self.is_template = bool(data["is_template"]) if "is_template" in data else False
		
		# Buttons
		self.buttons = {}
		for x in SCButtons:
			self.buttons[x] = self.parser.from_json_data(data["buttons"], x.name)
		# Pressing stick is interpreted as STICKPRESS button,
		# formely called just STICK
		if "STICK" in data["buttons"] and "STICKPRESS" not in data["buttons"]:
			self.buttons[SCButtons.STICKPRESS] = self.parser.from_json_data(
					data["buttons"], "STICK")
		
		# Stick & gyro
		self.stick = self.parser.from_json_data(data, "stick")
		self.gyro = self.parser.from_json_data(data, "gyro")
		
		if "triggers" in data:
			# Old format
			# Triggers
			self.triggers = ({
				x : self.parser.from_json_data(data["triggers"], x) for x in Profile.TRIGGERS
			})
			
			# Pads
			self.pads = {
				Profile.LEFT	: self.parser.from_json_data(data, "left_pad"),
				Profile.RIGHT	: self.parser.from_json_data(data, "right_pad"),
				Profile.CPAD	: NoAction()
			}
		else:
			# New format
			# Triggers
			self.triggers = {
				Profile.LEFT	: self.parser.from_json_data(data, "trigger_left"),
				Profile.RIGHT	: self.parser.from_json_data(data, "trigger_right"),
			}
			
			# Pads
			self.pads = {
				Profile.LEFT	: self.parser.from_json_data(data, "pad_left"),
				Profile.RIGHT	: self.parser.from_json_data(data, "pad_right"),
				Profile.CPAD	: self.parser.from_json_data(data, "cpad"),
			}
		
		# Menus
		self.menus = {}
		if "menus" in data:
			for id in data["menus"]:
				for invalid_char in ".:/":
					if invalid_char in id:
						raise ValueError("Invalid character '%s' in menu id '%s'" % (invalid_char, id))
				self.menus[id] = MenuData.from_json_data(data["menus"][id], self.parser)
		
		# Conversion
		self.original_version = version		# TODO: This is temporary
		if version < Profile.VERSION:
			self._convert(version)
		
		return self
	
	
	def clear(self):
		""" Clears all actions and adds default menu action on center button """
		self.buttons = { x : NoAction() for x in SCButtons }
		self.buttons[SCButtons.C] = HoldModifier(
			MenuAction("Default.menu"),
			normalaction = MenuAction("Default.menu")
		)
		self.menus = {}
		self.stick = NoAction()
		self.is_template = False
		self.triggers = { Profile.LEFT : NoAction(), Profile.RIGHT : NoAction() }
		self.pads = { Profile.LEFT : NoAction(),
				Profile.RIGHT : NoAction(), Profile.CPAD : NoAction() }
		self.gyro = NoAction()
	
	
	def get_all_actions(self):
		"""
		Returns generator with every action defined in this profile,
		including actions in menus.
		Recursively walks into macros, dpads and everything else that can have
		nested actions, so both parent and all child actions are yielded.
		
		May yield NoAction, but shouldn't yield None.
		
		Used for checks when profile is exported or imported.
		"""
		for action in self.get_actions():
			for i in action.get_all_actions():
				yield i
		for id in self.menus:
			for i in self.menus[id].get_all_actions():
				yield i
	
	
	def get_actions(self):
		"""
		As get_all_actions, but returns only root actions, without children,
		and ignores menus.
		"""
		for dct in (self.buttons, self.triggers, self.pads):
			for k in dct:
				yield dct[k]
		for action in (self.stick, self.gyro):
			yield action
	
	
	def get_filename(self):
		"""
		Returns filename of last loaded file or None.
		"""
		return self.filename
	
	
	def compress(self):
		"""
		Calls compress on every action to throw out some redundant stuff.
		Note that calling save() after compress() will break stuff.
		"""
		for dct in (self.buttons, self.triggers, self.pads):
			for x in dct:
				dct[x] = dct[x].compress()
		self.stick = self.stick.compress()
		self.gyro = self.gyro.compress()
		for menu in self.menus.values():
			menu.compress()
	
	
	def _convert(self, from_version):
		""" Performs conversion from older profile version """
		if from_version < 1:
			from scc.modifiers import ModeModifier
			# Add 'display Default.menu if center button is held' for old profiles
			c = self.buttons[SCButtons.C]
			if not c:
				# Nothing set to C button
				self.buttons[SCButtons.C] = HoldModifier(
					MenuAction("Default.menu"),
					normalaction = MenuAction("Default.menu")
				)
			elif hasattr(c, "holdaction") and c.holdaction:
				# Already set to something, don't overwrite it
				pass
			elif c.to_string().startswith("OSK."):
				# Special case, don't touch this either
				pass
			else:
				self.buttons[SCButtons.C] = HoldModifier(
					MenuAction("Default.menu"),
					normalaction = self.buttons[SCButtons.C]
				)
		if from_version < 1.1:
			# Convert old scrolling wheel to new representation
			from scc.modifiers import FeedbackModifier, BallModifier
			from scc.actions import MouseAction, XYAction
			from scc.uinput import Rels
			iswheelaction = ( lambda x : isinstance(x, MouseAction) and
					x.parameters[0] in (Rels.REL_HWHEEL, Rels.REL_WHEEL) )
			for p in (Profile.LEFT, Profile.RIGHT):
				a, feedback = self.pads[p], None
				if isinstance(a, FeedbackModifier):
					feedback = a.haptic.get_position()
					a = a.action
				if isinstance(a, XYAction):
					if iswheelaction(a.x) or iswheelaction(a.y):
						n = BallModifier(XYAction(a.x, a.y))
						if feedback is not None:
							n = FeedbackModifier(feedback, 4096, 16, n)
						self.pads[p] = n
						log.info("Converted %s to %s", a.to_string(), n.to_string())
		if from_version < 1.2:
			# Convert old trigger settings that were done with ButtonAction
			# to new TriggerAction
			from scc.constants import TRIGGER_HALF, TRIGGER_MAX, TRIGGER_CLICK
			from scc.actions import ButtonAction, TriggerAction, MultiAction
			from scc.uinput import Keys
			for p in (Profile.LEFT, Profile.RIGHT):
				if isinstance(self.triggers[p], ButtonAction):
					buttons, numbers = [], []
					n = None
					# There were one or two keys and zero to two numeric
					# parameters for old button action
					for param in self.triggers[p].parameters:
						if param in Keys:
							buttons.append(param)
						elif type(param) in (int, float):
							numbers.append(int(param))
					if len(numbers) == 0:
						# Trigger range was not specified, assume defaults
						numbers = ( TRIGGER_HALF, TRIGGER_CLICK )
					elif len(numbers) == 1:
						# Only lower range was specified, add default upper range
						numbers.append(TRIGGER_CLICK)
					if len(buttons) == 1:
						# If only one button was set, trigger should work like
						# one big button
						n = TriggerAction(numbers[0], ButtonAction(buttons[0]))
					elif len(buttons) == 2:
						# Both buttons were set
						n = MultiAction(
							TriggerAction(numbers[0], numbers[1], ButtonAction(buttons[0])),
							TriggerAction(numbers[1], TRIGGER_MAX, ButtonAction(buttons[1]))
						)
					
					if n:
						log.info("Converted %s to %s",
							self.triggers[p].to_string(), n.to_string())
						self.triggers[p] = n
		if from_version < 1.3:
			# Action format completly changed in v0.4, but profile foramat is same.
			pass