diff --git a/generate_svg.py b/generate_svg.py
index 18ec197f1..d02ff964a 100755
--- a/generate_svg.py
+++ b/generate_svg.py
@@ -165,7 +165,7 @@ def calculate(self, gen):
def place(self, gen, root):
e = SVGEditor.add_element(root, "rect",
- style = "opacity:1;fill-opacity:1.0;stroke-width:2.0;",
+ style = "opacity:1;fill-opacity:0.0;stroke-width:2.0;",
fill="#000000",
stroke="#06a400",
id = "box_%s" % (self.name,),
@@ -259,8 +259,9 @@ def __init__(self):
boxes.append(box_bcs)
- box_left = Box(self.PADDING, self.PADDING, Align.LEFT | Align.TOP,
- "left", min_height = self.full_height * 0.5)
+ box_left = Box(self.PADDING, self.PADDING, Align.LEFT | Align.TOP, "left",
+ min_height = self.full_height * 0.5,
+ min_width = self.full_width * 0.2)
box_left.add("LEFT", Action.AC_TRIGGER, profile.triggers.get(profile.LEFT))
box_left.add("LB", Action.AC_BUTTON, profile.buttons.get(SCButtons.LB))
box_left.add("LGRIP", Action.AC_BUTTON, profile.buttons.get(SCButtons.LGRIP))
@@ -268,8 +269,9 @@ def __init__(self):
boxes.append(box_left)
- box_right = Box(self.PADDING, self.PADDING, Align.RIGHT | Align.TOP,
- "right", min_height = self.full_height * 0.5)
+ box_right = Box(self.PADDING, self.PADDING, Align.RIGHT | Align.TOP, "right",
+ min_height = self.full_height * 0.5,
+ min_width = self.full_width * 0.2)
box_right.add("RIGHT", Action.AC_TRIGGER, profile.triggers.get(profile.RIGHT))
box_right.add("RB", Action.AC_BUTTON, profile.buttons.get(SCButtons.RB))
box_right.add("RGRIP", Action.AC_BUTTON, profile.buttons.get(SCButtons.RGRIP))
diff --git a/images/binding-display.svg b/images/binding-display.svg
index 5e4f0be5f..3024fff50 100644
--- a/images/binding-display.svg
+++ b/images/binding-display.svg
@@ -68,20 +68,21 @@
-
-
-
-
+
+
+
+
-
+
diff --git a/scc/gui/svg_widget.py b/scc/gui/svg_widget.py
index e2c45e500..e2a80129d 100644
--- a/scc/gui/svg_widget.py
+++ b/scc/gui/svg_widget.py
@@ -41,6 +41,7 @@ def __init__(self, app, filename, init_hilighted=True):
self.current_svg = open(filename, "r").read().decode("utf-8")
self.image_width = 1
+ self.image_height = 1
self.image = Gtk.Image()
self.parse_image()
if init_hilighted:
@@ -58,7 +59,8 @@ def parse_image(self):
"""
tree = ET.fromstring(self.current_svg.encode("utf-8"))
SVGWidget.find_areas(tree, (0, 0), self.areas)
- self.image_width = float(tree.attrib["width"])
+ self.image_width = float(tree.attrib["width"])
+ self.image_height = float(tree.attrib["height"])
def on_mouse_click(self, trash, event):
diff --git a/scc/osd/__init__.py b/scc/osd/__init__.py
index 48aa5d72e..a557d62a0 100644
--- a/scc/osd/__init__.py
+++ b/scc/osd/__init__.py
@@ -255,24 +255,35 @@ def make_window_clicktrough(self):
X.destroy_region (dpy, reg)
- def compute_position(self):
- """ Adjusts position for currently active screen (display) """
- x, y = self.position
+ def get_active_screen_geometry(self):
+ """
+ Returns geometry of active screen or None if active screen
+ cannot be determined.
+ """
screen = self.get_window().get_screen()
active_window = screen.get_active_window()
- width, height = self.get_window_size()
if active_window:
monitor = screen.get_monitor_at_window(active_window)
if monitor is not None:
- geometry = screen.get_monitor_geometry(monitor)
- if x < 0:
- x = x + geometry.x + geometry.width - width
- else:
- x = x + geometry.x
- if y < 0:
- y = y + geometry.y + geometry.height - height
- else:
- y = geometry.y + y
+ return screen.get_monitor_geometry(monitor)
+ return None
+
+
+ def compute_position(self):
+ """ Adjusts position for currently active screen (display) """
+ x, y = self.position
+ width, height = self.get_window_size()
+ geometry = self.get_active_screen_geometry()
+ if geometry:
+ if x < 0:
+ x = x + geometry.x + geometry.width - width
+ else:
+ x = x + geometry.x
+ if y < 0:
+ y = y + geometry.y + geometry.height - height
+ else:
+ y = geometry.y + y
+
return x, y
diff --git a/scc/osd/binding_display.py b/scc/osd/binding_display.py
new file mode 100644
index 000000000..8cea93260
--- /dev/null
+++ b/scc/osd/binding_display.py
@@ -0,0 +1,188 @@
+#!/usr/bin/env python2
+"""
+SC-Controller - OSD Launcher
+
+Display launcher with phone-like keyboard that user can use to select
+application (list is generated using xdg) and start it.
+
+Reuses styles from OSD Menu and OSD Dialog
+"""
+from __future__ import unicode_literals
+from scc.tools import _, set_logging_level
+
+from gi.repository import Gtk, Gio, Gdk, GdkX11, GdkPixbuf, Pango
+from scc.constants import STICK_PAD_MIN, STICK_PAD_MAX, SCButtons
+from scc.constants import LEFT, RIGHT, SAME, STICK
+from scc.paths import get_share_path, get_config_path
+from scc.menu_data import MenuData, MenuItem
+from scc.tools import point_in_gtkrect
+from scc.lib import xwrappers as X
+from scc.config import Config
+from scc.gui.svg_widget import SVGWidget, SVGEditor
+from scc.gui.daemon_manager import DaemonManager
+from scc.osd.timermanager import TimerManager
+from scc.osd import OSDWindow
+import os, sys, re, logging
+log = logging.getLogger("osd.binds")
+
+
+class BindingDisplay(OSDWindow, TimerManager):
+
+ def __init__(self, config=None):
+ self.bdisplay = os.path.join(get_config_path(), 'binding-display.svg')
+ if not os.path.exists(self.bdisplay):
+ # Prefer image in ~/.config/scc, but load default one as fallback
+ self.bdisplay = os.path.join(get_share_path(), "images", 'binding-display.svg')
+
+ OSDWindow.__init__(self, "osd-keyboard")
+ TimerManager.__init__(self)
+ self.daemon = None
+ self.config = config or Config()
+ self.group = None
+ self.limits = {}
+ self.background = None
+
+ self._eh_ids = []
+ self._stick = 0, 0
+
+ self.c = Gtk.Box()
+ self.c.set_name("osd-keyboard-container")
+
+
+ def _create_background(self):
+ self.background = SVGWidget(self, self.args.image, init_hilighted=False)
+ self._pack()
+
+
+ def _pack(self):
+ self.c.add(self.background)
+ self.add(self.c)
+
+
+ def use_daemon(self, d):
+ """
+ Allows (re)using already existing DaemonManager instance in same process
+ """
+ self.daemon = d
+ self._cononect_handlers()
+ self.on_daemon_connected(self.daemon)
+
+
+ def _add_arguments(self):
+ OSDWindow._add_arguments(self)
+ self.argparser.add_argument('image', type=str, nargs="?",
+ default = self.bdisplay, help="keyboard image to use")
+
+
+ def compute_position(self):
+ """
+ Unlike other OSD windows, this one is scaled to 80% of screen size
+ and centered in on active screen.
+ """
+ x, y = 10, 10
+ iw, ih = self.background.image_width, self.background.image_height
+ geometry = self.get_active_screen_geometry()
+ if geometry:
+ width = geometry.width * 0.8
+ height = int(float(ih) / float(iw) * float(width))
+ self.background.set_size_request(width, height)
+ x = geometry.x + ((geometry.width - width) / 2)
+ y = geometry.y + ((geometry.height - height) / 2)
+ return x, y
+
+
+ def recolor(self):
+ #iw, ih = self.background.image_width, self.background.image_height
+ #geometry = self.get_active_screen_geometry()
+ #if geometry:
+ # scale = (geometry.width * 0.8) / float(iw)
+ # editor = self.background.edit()
+ # SVGEditor.scale(SVGEditor.get_element(editor, "image"), scale)
+ # editor.commit()
+ self.background.edit().commit()
+ self.move(*self.compute_position())
+
+
+ def parse_argumets(self, argv):
+ if not OSDWindow.parse_argumets(self, argv):
+ return False
+ return True
+
+
+ def _cononect_handlers(self):
+ self._eh_ids += [
+ ( self.daemon, self.daemon.connect('dead', self.on_daemon_died) ),
+ ( self.daemon, self.daemon.connect('error', self.on_daemon_died) ),
+ ( self.daemon, self.daemon.connect('alive', self.on_daemon_connected) ),
+ ]
+
+
+ def run(self):
+ self.daemon = DaemonManager()
+ self._cononect_handlers()
+ OSDWindow.run(self)
+
+
+ def on_daemon_died(self, *a):
+ log.error("Daemon died")
+ self.quit(2)
+
+
+ def on_failed_to_lock(self, error):
+ log.error("Failed to lock input: %s", error)
+ self.quit(3)
+
+
+ def on_daemon_connected(self, *a):
+ def success(*a):
+ log.info("Sucessfully locked input")
+ pass
+
+ c = self.choose_controller(self.daemon)
+ if c is None or not c.is_connected():
+ # There is no controller connected to daemon
+ self.on_failed_to_lock("Controller not connected")
+ return
+
+ self._eh_ids += [ (c, c.connect('event', self.on_event)) ]
+ # Lock everything
+ locks = [ LEFT, RIGHT, STICK, "STICKPRESS" ] + [ b.name for b in SCButtons ]
+ c.lock(success, self.on_failed_to_lock, *locks)
+
+
+ def quit(self, code=-1):
+ if self.get_controller():
+ self.get_controller().unlock_all()
+ for source, eid in self._eh_ids:
+ source.disconnect(eid)
+ self._eh_ids = []
+ OSDWindow.quit(self, code)
+
+
+ def show(self, *a):
+ if self.background is None:
+ self._create_background()
+ OSDWindow.show(self, *a)
+ self.timer('recolor', 0.1, self.recolor)
+
+
+ def on_event(self, daemon, what, data):
+ """
+ Called when button press, button release or stick / pad update is
+ send by daemon.
+ """
+ pass
+
+
+def main():
+ m = BindingDisplay()
+ if not m.parse_argumets(sys.argv):
+ sys.exit(1)
+ m.run()
+ sys.exit(m.get_exit_code())
+
+
+if __name__ == "__main__":
+ from scc.tools import init_logging
+ init_logging()
+ main()